flutter中Mobx的使用及原理

Mobx是什么

mobx的作用可以使用一句话来概括:使用透明的函数响应式编程增强Dart程序中的状态管理,是前端中大名鼎鼎的Mobx.js的Dart版本。

Mobx的三个重要概念

  • Observables: 表示响应式的状态,也可理解为可观察对象。状态指的是应用程序里面的状态或者数据。响应式就是可以感知到、可观察到数据的变化,也就是我们经常接触到的观察者模式。
  • Actions: Actions就是一系列可以引发状态发生变化的动作。
  • Reactions: 状态的观察者,状态发生变化的时候,他们可以收到数据变化的通知。

具体用法

以计数器为例,一个简单的计数器可以表示成一个可观察的数字状态,计数器表示为Counter对象。

part 'counter.g.dart';

class Counter = CounterBase with _$Counter;

abstract class CounterBase with Store {
  @observable
  int value = 0;

  @action
  void increment() {
    value++;
  }
}

这里,Mobx需要借助builder_runner这个库生成对应的_$Counter类的代码。
计数器中的值需要通过Flutter_Mobx里面的Widget来反应到UI,使用Observer来包裹需要观察的组件:

final counter = Counter(); // Instantiate the store

Observer(
    builder: (_) => Text('${counter.value}',
    style: Theme.of(context).textTheme.display1,
    ),
),

这里使用了一个叫做Observer的Widget,builder方法中把counter的observable对象的值作为属性传给Text。

官方建议

官方建议,将widget-store-service结合在一起。
Widget:UI,状态的可视化表示
Store: 处理状态
Service: 逻辑操作,包括复杂逻辑,网络请求,本地数据库存储等等。

  1. UI层应该尽量使用StatelessWidget和Observer结合,减少Widget的rebuild次数,提升性能。
  2. Store里面放的 @observable 对象,因为 Dart 在 Flutter 是不能进行运行时反射的,所以复杂对象需要我们自己进行 observable 的声明。否则不会生效。当需要处理衍生状态的时候,可用 computed 替代。

关于不同的页面如何持有Store对象的问题

最简单的是直接写单例的store,但是单例的弊端非常明显,我们需要的是在这几个页面中此对象是同一个,超出这个范围,对象可以销毁,或者使用的是另一个对象,很直接的我们就会需要一个对象管理框架,即依赖注入。
针对这点,官方给出的建议是,可以使用Provider这个框架达到依赖注入的目的,可以查看https://pub.dev/packages/provider 。

原理

首先,在上面的计数器例子中,看到新生成的类_$Counter,其中部分代码如下:

@override
  int get value {
    _$valueAtom.reportRead();
    return super.value;
  }

  @override
  set value(int value) {
    _$valueAtom.reportWrite(value, super.value, () {
      super.value = value;
    });
  }

可以看到在获取变量时,会调用reportRead(), 设置变量会调用reportWrite
我们先看看reportRead()做了什么,

void reportRead() {
    context.enforceReadPolicy(this);//1
    reportObserved();//2
  }

第一行代码是为了确保观察值在Actions和Reactions内部读取。重点在于第二行调用的reportObserved方法,其代码如下:

//atom可以理解为对应的被观察对象的封装
void _reportObserved(Atom atom) {
    final derivation = _state.trackingDerivation;

    if (derivation != null) {
      derivation._newObservables.add(atom);
      if (!atom._isBeingObserved) {
        atom
          .._isBeingObserved = true
          .._notifyOnBecomeObserved();
      }
    }
  }

可以看出来这段代码做的事情就是:把当前的变量加入到被观察的队列中去,如果变量未被观察,则把此变量设置为观察状态。
再来看看reportWrite做了什么:

void reportWrite(T newValue, T oldValue, void Function() setNewValue) {
    context.spyReport(ObservableValueSpyEvent(this,
        newValue: newValue, oldValue: oldValue, name: name));

    // ignore: cascade_invocations
    context.conditionallyRunInAction(() {
      setNewValue();
      reportChanged();
    }, this, name: '${name}_set');

    // ignore: cascade_invocations
    context.spyReport(EndedSpyEvent(type: 'observable', name: name));
  }

其核心是执行了reportChanged方法,

  void reportChanged() {
    _context
      ..startBatch()
      ..propagateChanged(this)
      ..endBatch();
  }

来看看propagateChanged()中都做了些什么:

void propagateChanged(Atom atom) {
    if (atom._lowestObserverState == DerivationState.stale) {
      return;
    }

    atom._lowestObserverState = DerivationState.stale;

    for (final observer in atom._observers) {
      if (observer._dependenciesState == DerivationState.upToDate) {
        observer._onBecomeStale();//核心
      }
      observer._dependenciesState = DerivationState.stale;
    }
  }

当数据需要更新的时候,调用观察者的_onBecomeState方法。查看observable.dart并追踪下去(过程过于复杂,这里不多赘述),可以发现最终就是调用了reaction的run方法,

for (final reaction in remainingReactions) {
        reaction._run();
      }

如下:


  @override
  void _run() {
    if (_isDisposed) {
      return;
    }

    _context.startBatch();

    _isScheduled = false;

    if (_context._shouldCompute(this)) {
      try {
        _onInvalidate();
      } on Object catch (e) {
        // Note: "on Object" accounts for both Error and Exception
        _errorValue = MobXCaughtException(e);
        _reportException(e);
      }
    }

    _context.endBatch();
  }

其中的_onInvalidate()就是在observer构成的时候传入的方法:

  void _invalidate() => setState(noOp);

  static void noOp() {}

看到这里,我们发现就是通过调用setState从而刷新了widget。

参考资料

  1. https://juejin.cn/post/6844903860184563720
  2. https://takeroro.github.io/2020/06/30/mobX%20flutter%20%E6%95%B0%E6%8D%AE%E6%B5%81%E5%8A%A8/
  3. https://cn.mobx.js.org/

你可能感兴趣的:(flutter中Mobx的使用及原理)