接上一篇Flutter状态管理之路(三) 此篇主要介绍flutter_mobx
flutter_mobx 版本:
dependencies: mobx: ^0.4.0 flutter_mobx: ^0.3.4
dev_dependencies: build_runner: ^1.3.1 mobx_codegen: ^0.3.11
文档:https://mobx.pub/
概念
对象
说明
Observables
代表响应式状态,可以是普通dart对象, 也可以是一颗状态树,变化会触发reaction
Computed
计算属性,根据多个Observables来源计算出 其应该输出的值,有缓存,不使用会清空, 源改变会触发重新计算,变化也会触发reaction
Actions
响应改变Observables的地方
Reactions
对Action、Observable、Computed三元素响应的地方, 可以是Widget/函数
Observer
上述Reaction的一个具体实现,用于Flutter中包裹需要响应 Observable的子树
概念图(来自mobx.pub):
图来自mobx.pub
使用例子 来自官网 计数器Demo
定义Store,新建counter.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // Include generated file part 'counter.g.dart'; /// 利用注解解析生成代码 // This is the class used by rest of your codebase class Counter = _Counter with _$Counter; // The store-class abstract class _Counter with Store { @observable int value = 0; @action void increment() { value++; } }
main.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 final counter = Counter(); // 1. 初始化Store void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'MobX', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('MobX Counter'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), // Wrapping in the Observer will automatically re-render on changes to counter.value Observer( /// 2. 用Observer包裹 使用counter 会自动建立订阅关系 builder: (_) => Text( '${counter.value}', style: Theme.of(context).textTheme.display1, ), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: counter.increment, /// 3. 调用Observer的setter方法 通知更新 tooltip: 'Increment', child: Icon(Icons.add), ), ); } }
图示
flutter_mobx主流程图1.png
关键对象 以上述计数器例子来分析下源码中关键对象
_$Counter 该对象由代码生成器生成,主要实现扩展自定义的dart object的Observable的能力
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 mixin _$Counter on _Counter, Store { /// Store只是起标识作用的mixin /// _Counter是我们自定义的状态对象 final _$valueAtom = Atom(name: '_Counter.value'); /// 根据@observable标识的变量,生成的对应的Atom对象,用于与Reactions实现观察者模式 @override int get value { /// 属性value的getter 根据@observable生成 _$valueAtom.context.enforceReadPolicy(_$valueAtom); /// 用于限制此方法是否能在非Actions和Reactions里调用,默认允许,否则抛出assert异常 _$valueAtom.reportObserved(); /// 注册观察者(Reaction) return super.value; /// 返回值 } @override set value(int value) { _$valueAtom.context.conditionallyRunInAction(() { /// conditionallyRunInAction 用于判断写入的安全策略和是否包含action的追踪 super.value = value; /// 赋值的地方 _$valueAtom.reportChanged(); /// 通知注册在valueAtom里的Observer刷新数据 }, _$valueAtom, name: '${_$valueAtom.name}_set'); } final _$_CounterActionController = ActionController(name: '_Counter'); /// 根据@action生成,用于记录action调用情况 @override void increment() { final _$actionInfo = _$_CounterActionController.startAction(); /// 记录开始 try { return super.increment(); /// 真正执行定义的increment方法 } finally { _$_CounterActionController.endAction(_$actionInfo); /// 记录完成 } } }
上述代码片段里的_$valueAtom.context
是每个Atom里默认取的全局的MainContext,看Atom构造:
1 2 3 4 5 6 7 8 9 10 class Atom { factory Atom( {String name, Function() onObserved, Function() onUnobserved, ReactiveContext context}) => Atom._(context ?? mainContext, /// 注意此处,参数不传会使用mainContext name: name, onObserved: onObserved, onUnobserved: onUnobserved); ... }
看下几个重点方法:
_$valueAtom.context.conditionallyRunInAction
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void conditionallyRunInAction(void Function() fn, Atom atom, {String name, ActionController actionController}) { if (isWithinBatch) { /// 当在action、reaction里执行时,直接进入此处 enforceWritePolicy(atom); /// 检查写入权限,如是否可在非action外进行写入等 fn(); /// 执行真正赋值的地方 } else { /// 非 action or transaction 里执行走这 final controller = actionController ?? ActionController( context: this, name: name ?? nameFor('conditionallyRunInAction')); final runInfo = controller.startAction(); /// 记录action开始 try { enforceWritePolicy(atom); fn(); } finally { controller.endAction(runInfo); /// 记录action结束 } } }
_$valueAtom.reportObserved()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /// Atom void reportObserved() { _context._reportObserved(this); } /// ReactiveContext void _reportObserved(Atom atom) { final derivation = _state.trackingDerivation; /// 取出当前正在执行的reactions or computeds if (derivation != null) { derivation._newObservables.add(atom); /// 将当前atom绑进derivation里 if (!atom._isBeingObserved) { /// 如果atom之前并没有被加入观察,则执行此处 atom .._isBeingObserved = true .._notifyOnBecomeObserved(); /// 通知Observable 的所有listener - 其变为被观察状态 } } }
上面可以看出,atom被加入到当前reaction(derivation)的监听集合里,即reaction持有了atom,但是atom改变时是需要通知到reaction的,继续看下面
_$valueAtom.reportChanged()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 /// Atom void reportChanged() { _context ..startBatch() /// batch计数+1 ,记录当前batch的深度,用来追踪如action执行的深度 ..propagateChanged(this) /// 通知注册在atom里的observer(即Derivation)数据改变 ..endBatch(); /// 执行完毕,batch计数-1并检查batch执行深度是否归0,此处是做了层优化 } /// ReactiveContext void propagateChanged(Atom atom) { ... atom._lowestObserverState = DerivationState.stale; for (final observer in atom._observers) { if (observer._dependenciesState == DerivationState.upToDate) { observer._onBecomeStale(); /// 通知所有注册的即Derivation数据改变 } observer._dependenciesState = DerivationState.stale; } } void endBatch() { if (--_state.batch == 0) { /// 优化:当前执行改变的层次没回归0时,跳过最终的reaction响应,只有全部执行完毕才走下面的逻辑 (个人理解:因为是单线程,此处考虑的应该是递归情况,如action里再调用action) runReactions(); /// 通知挂起的reactions 数据改变 /// List<Reaction> pendingReactions = []; /// The reactions that must be triggered at the end of a `transaction` or an `action` for (var i = 0; i < _state.pendingUnobservations.length; i++) { /// 这里处理断开连接的observations 如dispose掉 final ob = _state.pendingUnobservations[i] .._isPendingUnobservation = false; if (ob._observers.isEmpty) { if (ob._isBeingObserved) { // if this observable had reactive observers, trigger the hooks ob .._isBeingObserved = false .._notifyOnBecomeUnobserved(); } if (ob is Computed) { ob._suspend(); } } } _state.pendingUnobservations = []; } }
基本上,_$Counter
就是对@observable
注解的变量扩展getter、setter方法,getter里将变量对应的atom绑进当前执行的derivation
里去;在setter里去通知atom里的_observers
集合。
@action
注解的方法,则会被包含进_$_CounterActionController
控制里,记录action执行情况
但是atom._observers里的元素是什么时候注册的,按照mobx的理念是在reaction里引用过Observable,则自动tracking,所以接下来看Observer
Observer flutter中作为UI的响应式组件,简单看下类图
mobx_observer.png
如上图,StatelessObserverWidget extends StatelessWidget,框架主要通过ObserverWidgetMixin
和ObserverElementMixin
来扩展功能
ObserverWidgetMixin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mixin ObserverWidgetMixin on Widget { String getName(); ReactiveContext getContext() => mainContext; Reaction createReaction( Function() onInvalidate, { Function(Object, Reaction) onError, }) => ReactionImpl( getContext(), onInvalidate, name: getName(), onError: onError, ); }
基本上就是扩展了 1) 创建Reaction 2) 获取mainContext 全局响应式上下文
ObserverElementMixin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 mixin ObserverElementMixin on ComponentElement { ReactionImpl get reaction => _reaction; ReactionImpl _reaction; /// 包裹的响应类 ObserverWidgetMixin get _widget => widget as ObserverWidgetMixin; @override void mount(Element parent, dynamic newSlot) { /// 挂载Element 时 创建Reaction _reaction = _widget.createReaction(invalidate, onError: (e, _) { FlutterError.reportError(FlutterErrorDetails( library: 'flutter_mobx', exception: e, stack: e is Error ? e.stackTrace : null, )); }) as ReactionImpl; super.mount(parent, newSlot); } void invalidate() => markNeedsBuild(); /// Observable改变时会通知到这里 标脏 @override Widget build() { Widget built; reaction.track(() { /// 每次挂载上Element树上会启动reaction的track,在这里面建立在传入的build方法里(即Observer的build属性) 获取过的Observable的关联 built = super.build(); /// 调用外部传入的build方法 建立Widget子树 }); ... return built; } @override void unmount() { /// 卸载Element 时 卸载Reaction reaction.dispose(); super.unmount(); } }
接下来重点看reaction.track
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /// ReactionImpl void track(void Function() fn) { _context.startBatch(); /// batch次数+1 _isRunning = true; _context.trackDerivation(this, fn); /// 开始追踪这个derivation即此时的reaction _isRunning = false; if (_isDisposed) { _context._clearObservables(this); /// dispose的话 清理 } if (_context._hasCaughtException(this)) { _reportException(_errorValue._exception); } _context.endBatch(); /// 此处理操作完成 }
进入_context.trackDerivation
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /// ReactiveContext T trackDerivation<T>(Derivation d, T Function() fn) { final prevDerivation = _startTracking(d); /// 让mainContext开始追踪传入的derivation T result; if (config.disableErrorBoundaries == true) { result = fn(); } else { try { result = fn(); /// 这里调用Observer里传入的build函数,里面会调用Observable的getter方法,上面提到的derivation就是这个d,所以atom会注册到这个d里面去 d._errorValue = null; } on Object catch (e) { d._errorValue = MobXCaughtException(e); } } _endTracking(d, prevDerivation); /// 结束追踪 return result; }
进入_startTracking(d)
1 2 3 4 5 6 7 8 9 10 /// ReactiveContext Derivation _startTracking(Derivation derivation) { final prevDerivation = _state.trackingDerivation; _state.trackingDerivation = derivation; /// 将传入的derivation赋值为当前正在追踪的,所以从这之后调用的Observable的getter方法里拿到的都是它 _resetDerivationState(derivation); /// 重置derivation状态 derivation._newObservables = {}; /// 清空,方便之后的atom加入 return prevDerivation; }
进入_endTracking(d, prevDerivation)
1 2 3 4 void _endTracking(Derivation currentDerivation, Derivation prevDerivation) { _state.trackingDerivation = prevDerivation; _bindDependencies(currentDerivation); /// 绑定derivation依赖的Observables }
进入_bindDependencies(currentDerivation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 void _bindDependencies(Derivation derivation) { final staleObservables = derivation._observables.difference(derivation._newObservables); /// 取出不一致的observable集合 final newObservables = derivation._newObservables.difference(derivation._observables); /// 取出新的observable集合 var lowestNewDerivationState = DerivationState.upToDate; // Add newly found observables for (final observable in newObservables) { observable._addObserver(derivation); /// 关键点1 这里将此derivation添加到Observable的_observers集合里,即在这里实现了atom持有derivation // Computed = Observable + Derivation if (observable is Computed) { if (observable._dependenciesState.index > lowestNewDerivationState.index) { lowestNewDerivationState = observable._dependenciesState; } } } // Remove previous observables for (final ob in staleObservables) { ob._removeObserver(derivation); } if (lowestNewDerivationState != DerivationState.upToDate) { derivation .._dependenciesState = lowestNewDerivationState .._onBecomeStale(); } derivation .._observables = derivation._newObservables .._newObservables = {}; // No need for newObservables beyond this point }
如上关键点1,将derivation里关联的observable拿到,并将derivation注入到每个observable里,这里为止实现了observable和derivation的双向绑定
Computed 该对象由@computed生成,充当Atom和Derivation的双重身份,即作为Atom给Observer等Reaction来观察,作为Derivation其方法里调用了其他的Atom,会监听其他的Atom的变化来触发自身的改变
看下自己定义的类
1 2 3 4 5 6 7 8 9 10 11 12 abstract class _Counter with Store { @observable int value = 0; @action void increment() { value++; } @computed int get testCmp => value + 1; }
生成的counter.g.dart
1 2 3 4 5 6 7 8 mixin _$Counter on _Counter, Store { Computed<int> _$testCmpComputed; @override int get testCmp => (_$testCmpComputed ??= Computed<int>(() => super.testCmp)).value; ... }
可以看出,实际就是生成了一个Computed来包裹我们定义的testCmp getter方法,下面看Computed的实现
1 2 3 4 5 6 7 8 class Computed<T> extends Atom implements Derivation, ObservableValue<T> { factory Computed(T Function() fn, {String name, ReactiveContext context}) => Computed._(context ?? mainContext, fn, name: name); Computed._(ReactiveContext context, this._fn, {String name}) : super._(context, name: name ?? context.nameFor('Computed')); ... }
可以看出,具备了Atom和Derivation的特性,构造函数里比Atom多持有了外部的传入的(即我们自己定义的)方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Computed<T> extends Atom implements Derivation, ObservableValue<T> { ... T _value; /// 缓存的value @override T get value { ... if (!_context.isWithinBatch && _observers.isEmpty) { /// 如果没在action or transaction里执行 并且 被其作为atom _observers内无观察者时 if (_context._shouldCompute(this)) { /// 判断其是否需要重新计算新的值,因为涉及到以及缓存 _context.startBatch(); _value = computeValue(track: false); /// 计算新的值 且不将自己作为derivation利用mainContext进行追踪 _context.endBatch(); } } else { reportObserved(); /// 自己作为atom 被reaction调用时,上报自己给derivation监听 if (_context._shouldCompute(this)) { if (_trackAndCompute()) { /// 开启reaction的追踪并计算新值 _context._propagateChangeConfirmed(this); /// 标记其持有的_observers 的依赖状态为脏 } } } ... return _value; } @override void _suspend() { _context._clearObservables(this); _value = null; /// 挂起时清除_value缓存值 } ... }
以上代码片段重点三个方法:_context._shouldCompute
、computeValue(track: false)
、_trackAndCompute()
_context._shouldCompute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 bool _shouldCompute(Derivation derivation) { switch (derivation._dependenciesState) { case DerivationState.upToDate: return false; case DerivationState.notTracking: case DerivationState.stale: return true; case DerivationState.possiblyStale: return untracked(() { for (final obs in derivation._observables) { /// 遍历其使用过的Atom if (obs is Computed) { /// 判断依赖的atom是否也是Computed,是的话需要处罚依赖去计算新的值 // Force a computation if (config.disableErrorBoundaries == true) { obs.value; /// 触发计算新的值 } else { try { obs.value; } on Object catch (_) { return true; } } if (derivation._dependenciesState == DerivationState.stale) { return true; } } } _resetDerivationState(derivation); return false; }); } return false; }
computeValue(track: false)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 T computeValue({bool track}) { _isComputing = true; ... T value; if (track) { value = _context.trackDerivation(this, _fn); /// 让computed作为derivation身份,调用_fn前利用ReactiveContext开启tracking模式 } else { ... value = _fn(); /// 计算获取新的值 ... } ... _isComputing = false; return value; }
_trackAndCompute()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 bool _trackAndCompute() { final oldValue = _value; final wasSuspended = _dependenciesState == DerivationState.notTracking; final newValue = computeValue(track: true); /// 计算新值通知开启ReactiveContext的追踪 final changed = wasSuspended || _context._hasCaughtException(this) || !_isEqual(oldValue, newValue); if (changed) { _value = newValue; /// 赋新值 } return changed; }
总结 优点:
observer的组件真正实现按需更新,只有监听的数据发生变化,它才会re-render
具备Computer计算属性机制,无引用时会自动回收
使用注解省去了notify等模板代码,简洁
mobx耦合性更低
缺点:
store过多导致无法统一数据源,管理是个问题
没有时间回溯能力,因为数据只有一份引用
缺乏中间件机制有效支持