接上一篇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
图来自mobx.pub

使用例子

来自官网 计数器Demo

  1. 定义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++;
}
}
  1. 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
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);
...
}

看下几个重点方法:

  1. _$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结束
}
}
}
  1. _$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的,继续看下面

  1. _$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
mobx_observer.png

如上图,StatelessObserverWidget extends StatelessWidget,框架主要通过ObserverWidgetMixinObserverElementMixin来扩展功能

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

  1. 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._shouldComputecomputeValue(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;
}

总结

优点:

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

缺点:

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