文本是 mobx 源码解读系列 第三篇
本系列文章全部采用 mobx 较新版本:v5.13.0
在阅读之前,希望你对以下技术有所了解或实践,不然可能会影响你对本文的理解
-
本来想直接从
autorun
讲的,发现有些坑还是要先填上,之前觉得ComputedValue
和ObservableValue
相似,后面发现大不相同 -
文本主要围绕
Derivation
,ComputedValue
,ObservableValue
讲讲mobx
中的订阅-发布模式 -
顺便梳理下把三者变化过程的
状态机
,为其他autorun
做铺垫
每点都很重要,牢记
mobx
中由Derivation
(reaction
也是实现它)充当观察者,其中observing
代表依赖的可观察对象集合,dependenciesState
代表观察者状态
ObservableValue
充当可观察对象,其中observers
代表依赖它的观察者集合,lowestObserverState
代表可观察对象的状态(全文的 observable 代表 observableValue,observableObject,observableArray...)
ComputedValue
有点特殊同时实现了IDerivation
和IObservable
接口,所以它拥有上述两点的特征。因为ComputedValue
可以被Derivation
依赖,同时也可以依赖ObservableValue
class Store {
@observable num = 3;
@computed get mixed() {
return store.num + 1; // 依赖 observableValue
}
}
const store = new Store();
autorun(reaction => {
console.log("autorun:", store.mixed); // 被 Derivation 依赖
});
-
要注意的是
ComputedValue
作为可观察对象向Derivation
提交变化时,要使用observing
和lowestObserverState
。相反作为观察者时,用observers
和dependenciesState
-
DerivationState
状态机,状态越高表示越不稳定(POSSIBLY_STALE、STALE),相反同理(UP_TO_DATE)
- autorun 会调用 reaction.schedule 方法将其 push 到 globalState.pendingReactions
再调用 runReactions 遍历 pendingReactions 逐一收集 reaction 的依赖
- runReactions 中通过 while 取出 pendingReactions 来执行它们的 runReaction 方法(下章会说)
用 while 是因为在此轮收集依赖过程中可能会有其他的 reaction 加入
新增的 pendingReaction 也会调用 schedule,但会在 runReactions 的判断中将其 return 掉(因为 while 能拿到新增的 reaction)
runReaction 最终会掉用 trackDerivedFunction 开始依赖收集
- 收集依赖核心:trackDerivedFunction
先调用 changeDependenciesStateTo0 方法将 derivation 和 observing 置为稳定态 UP_TO_DATE,主要是方便后续判断是否处在收集依赖阶段(下章会讲)
trackingDerivation 为在全局正在收集依赖的 derivation,初始为 null
使用 newObserving 临时变量储存依赖和为后面的绑定依赖做准备
然后是“切分支”,见下代码,记住这种神迹的操作,后面会多次用到
其主要目的是保存正在收集依赖的 derivation,以防 f.call(context) 时递归调用了 trackDerivedFunction,而找不到上下文
聪明的你可能猜到了其中包含 ComputedValue
f.call(context) 正式执行 autorun 中传入的函数,newObserving 数组也是在这一阶段填充的
最后是 bindDependencies 绑定依赖
const prevTracking = globalState.trackingDerivation
globalState.trackingDerivation = derivation;
doSomething(); // use globalState.trackingDerivation do something fun
globalState.trackingDerivation = prevTracking;
- newObserving 储存依赖
回想下我们怎么使用 autorun 的:
const store = new Store();
autorun(reaction => {
console.log("autorun:", store.mixed);
});
如果你对 observable 敏感的话,肯定会反应过来:get 代理
在 observableValue 和 computedValue 的 get 代理通过调用 reportObserved 通知给 derivation
observable 的 reportObserved 能力是因为继承 Atom
- reportObserved 当然是调用 globalState.trackingDerivation 咯
- 看上面图,再说下 computedValue 的 get 代理
里面调用 trackAndCompute 就是刚说到的“切分支”的情况
如果 computedValue 还没被 derivation 依赖且没被计算过
就递归 trackDerivedFunction,传入 derivation(computed 属性)和 scope(store)
开始 computedValue 分支的依赖收集,收集完后再回到原来的 derivation
- 如果 trackAndCompute 的值有改变则调用 propagateChangeConfirmed
将自己置为“高状” STALE,并通知 observers 自己改变了,你们也去作出相应反应吧
需要注意,就是如果 d.dependenciesState 为 UP_TO_DATE,则说明这个 derivation 正在走 trackDerivedFunction
需要恢复 changeDependenciesStateTo0 的工作,将 observable 返回稳定态
- 既然说到了改变,那 observableValue 走 set 代理也同理
observableValue 不会递归 trackDerivedFunction,直接调用 d.onBecomeStale 变为不稳定态即可
f.call(context) 期间完成了 derivation 对 observable 依赖的收集
- 回到 trackDerivedFunction 的 bindDependencies
通过三个循环完成 observable 对 derivation 的收集和感知
① 遍历 newObserving 将未收集的依赖 diffValue 置为 1,之前收集过了则忽略
② 给 observable 解绑 derivation(看清楚顺序哦),即 observable 的改变不需要通知 derivation 了
③ 给新的 observable 绑定 derivation,代表它们的改变需通知 derivation,之前绑过则忽略
- addObserver 很简单
-
欢迎在 mobx 源码解读 issue 中讨论~
-
推荐:
minbx: mini mobx
,供学习使用:minbx 项目地址 -
下章剧透:讲讲 mobx 的 autorun 机制
-
码字不易,喜欢的记得点 ❤️ 哦