Flutter state management of the road (four)

Connect a Flutter state management of the road (c) of
this post describes flutter_mobx

flutter_mobx

version:

dependencies:
mobx: ^ 0.4.0
flutter_mobx: ^ 0.3.4

dev_dependencies:
build_runner: ^1.3.1
mobx_codegen: ^0.3.11

Documentation: https: //mobx.pub/

concept

Objects Explanation
Observables On behalf of a responsive state, can be an ordinary dart target,
may also be a state tree, the change will trigger reaction
Computed Calculating properties, calculated from the plurality of sources Observables
value to be output, there is a cache is not used is emptied
source changes trigger recomputation, also triggers changes in reaction
Actions Observables response to a change of place
Reactions The local Action, Observable, Computed three response elements,
may be Widget / function
Observer Reaction of the above-described one particular implementation, Flutter in response to the need for wrapping
Observable subtree

Conceptual diagram (from mobx.pub):

Figure from mobx.pub

Examples of use

From the official website counter Demo

  1. Definitions Store, New 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),
      ),
    );
  }
}

Icon

flutter_mobx main flowchart 1.png

Key Objects

In the example of the counter at the source code to analyze critical objects

_$Counter

Observable ability of the subject generated by the code generator, the main achieve the expansion of the custom dart object

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的双向绑定

总结

优点:

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

缺点:

  1. store too much lead can not be unified data source management is a problem
  2. No time backtracking capability, because the data is only a reference
  3. The lack of effective support mechanisms middleware
Published 15 original articles · won praise 11 · views 10000 +

Guess you like

Origin blog.csdn.net/Weiye__Lee/article/details/104042549