接上一篇Flutter状态管理之路(三)
此篇主要介绍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):
来自官网 计数器Demo
// 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++;
}
}
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: [
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),
),
);
}
}
以上述计数器例子来分析下源码中关键对象
该对象由代码生成器生成,主要实现扩展自定义的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 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
flutter中作为UI的响应式组件,简单看下类图
如上图,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(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的双向绑定
优点:
缺点: