Flutter状态管理之路(四)
接上一篇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
概念
对象 | 说明 | |
---|---|---|
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
- 定义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++;
}
}
- 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);
...
}
看下几个重点方法:
_$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结束
}
}
}
_$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的,继续看下面
_$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,框架主要通过ObserverWidgetMixin
和ObserverElementMixin
来扩展功能
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 全局响应式上下文
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._shouldCompute
、computeValue(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;
}
总结
优点:
- observer的组件真正实现按需更新,只有监听的数据发生变化,它才会re-render
- 具备Computer计算属性机制,无引用时会自动回收
- 使用注解省去了notify等模板代码,简洁
- mobx耦合性更低
缺点:
- store过多导致无法统一数据源,管理是个问题
- 没有时间回溯能力,因为数据只有一份引用
- 缺乏中间件机制有效支持