Flutter圈子FlutterFlutter中文社区

Flutter状态管理之路(四)

2020-01-19  本文已影响0人  Owen_Lee

接上一篇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. 缺乏中间件机制有效支持
上一篇下一篇

猜你喜欢

热点阅读