Flutter状态管理之路(四)

作者: Owen_Lee | 来源:发表于2020-01-19 15:53 被阅读0次

    接上一篇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对象,<br />也可以是一颗状态树,变化会触发reaction
    Computed 计算属性,根据多个Observables来源计算出<br />其应该输出的值,有缓存,不使用会清空,<br />源改变会触发重新计算,变化也会触发reaction
    Actions 响应改变Observables的地方
    Reactions 对Action、Observable、Computed三元素响应的地方,<br />可以是Widget/函数
    Observer 上述Reaction的一个具体实现,用于Flutter中包裹需要响应<br />Observable的子树

    概念图(来自mobx.pub):

    图来自mobx.pub

    使用例子

    来自官网 计数器Demo

    1. 定义Store,新建counter.dart
    
    
    // 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++;
      }
    }
    
    1. main.dart
    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的能力

    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构造:

    class Atom {
      factory Atom(
              {String name,
              Function() onObserved,
              Function() onUnobserved,
              ReactiveContext context}) =>
          Atom._(context ?? mainContext,   /// 注意此处,参数不传会使用mainContext
              name: name, onObserved: onObserved, onUnobserved: onUnobserved);
            ...
    }
    

    看下几个重点方法:

    1. _$valueAtom.context.conditionallyRunInAction
    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结束
          }
        }
      }
    
    1. _$valueAtom.reportObserved()
        /// 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的,继续看下面

    1. _$valueAtom.reportChanged()
        /// 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,框架主要通过ObserverWidgetMixinObserverElementMixin来扩展功能

    1. ObserverWidgetMixin
    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 全局响应式上下文

    1. ObserverElementMixin
    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

    /// 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方法

    /// 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)

      /// 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)

      void _endTracking(Derivation currentDerivation, Derivation prevDerivation) {
        _state.trackingDerivation = prevDerivation;
        _bindDependencies(currentDerivation);  /// 绑定derivation依赖的Observables
      }
    

    进入_bindDependencies(currentDerivation)

     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的变化来触发自身的改变

    看下自己定义的类

    abstract class _Counter with Store {
      @observable
      int value = 0;
    
      @action
      void increment() {
        value++;
      }
    
      @computed
      int get testCmp => value + 1;
    }
    

    生成的counter.g.dart

    mixin _$Counter on _Counter, Store {
      Computed<int> _$testCmpComputed;
    
      @override
      int get testCmp =>
          (_$testCmpComputed ??= Computed<int>(() => super.testCmp)).value;
      ...    
     }
    

    可以看出,实际就是生成了一个Computed来包裹我们定义的testCmp getter方法,下面看Computed的实现

    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多持有了外部的传入的(即我们自己定义的)方法

    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._shouldComputecomputeValue(track: false)_trackAndCompute()

    _context._shouldCompute

    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)

    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()

    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;
      }
    

    总结

    优点:

    1. observer的组件真正实现按需更新,只有监听的数据发生变化,它才会re-render
    2. 具备Computer计算属性机制,无引用时会自动回收
    3. 使用注解省去了notify等模板代码,简洁
    4. mobx耦合性更低

    缺点:

    1. store过多导致无法统一数据源,管理是个问题
    2. 没有时间回溯能力,因为数据只有一份引用
    3. 缺乏中间件机制有效支持

    相关文章

      网友评论

        本文标题:Flutter状态管理之路(四)

        本文链接:https://www.haomeiwen.com/subject/aebrzctx.html