Mobx是一个通过透明的函数响应式编程的状态管理库,一提到响应式,首先想到的就是如今响应式编程的代表Vue。两个在响应式的实现都选择了数据劫持的方法,就是将赋值的过程借助函数处理,实现一些额外的操作,比如记录日志,触发回调等等。Vue通过Object.defineProperty,Mobx同样也使用了Object.defineProperty(最新的Mobx5使用了Proxy实现),不同的是Mobx在这里只是用作创建代理对象,真正的响应式实现在observable实例上。
以下分析涉及代码为mobx4版本。
响应式数据
Mobx中,将响应式数据按类型可以分为,基本类型,Object,Array,Map。Object,Array,Map都是在基本类型的基础上的递归实现。其中几个关键类,ObservableValue继承自Atom类,是基本类型的响应式实现,提供了set/get方法。ObservableObjectAdministration实例作为代理,能将基本数据类型的赋值动作转变成响应式,提供了write/read方法,Mobx在对象上创建了一个ObservableObjectAdministration的实例$mobx代理,减少了对源数据的污染,通过Object.defineProperty,将对象的操作,委托给代理。ObservableArrayAdministration 实例能处理数组;ObservableMap 实例能处理 map 数据结构。

|
|
|
|
|
|
数组和Map的具体实现就不在这里介绍,具体可到源码中查看。
运行机制
在严格模式下Mobx的一次响应式过程包括,从执行动作 action 促使响应式数据 observable 变化,再到衍生 derivation 执行的整个过程。
- observable: 响应式数据。
- derivation: 衍生,响应式数据变更后执行的副作用函数,包含计算属性、反应,可以类比Vue中的watch。
- action: 动作,由其促使响应式数据发生变更。
action的意义在于,使用事务封装执行动作,将一组响应式数据变更复合为一,等到这组响应式数据变更完结后,才执行 derivation。
observable
继承自Atom类额外增加了reportObserve,reportChanged方法。reportObserve注册依赖,即当observable被观察时,类比Vue中depend;当observable数据变更时,会调用reportChanged方法,类比Vue中notify。
derivation
derivation即消费observable的观察者,在 mobx 实现中,observable, derivation 相互持有彼此的引用,当观察数据变更时,derivation将执行。
执行过程
在Vue中,注册的回调函数执行是通过内部的异步队列控制的,避免不必要的性能损耗,Mobx则通过事务管理更新操作。Mobx默认更新是同步的,obserable一旦发生变化,derivation就会执行。如果要是希望通线程中更新操作只触发一次,那直接使用action,将操作包裹起来就行,而实现的核心就是事务的运用。
|
|
|
|
Mobx中的事务通过 globalState.inBatch 计数器标识:startBatch阶段,globalState.inBatch加 1;endBatch阶段,globalState.inBatch减 1;当globalState.inBatch为 0 时,表示单个事务周期结束,事务的意义就在于将并行的响应式数据变更视为一组,在一组变更完成之后,才执行相应的衍生,这样就保证了同步更新,但衍生只执行一次。
最后
除了上述提到的响应式实现不同,Mobx和Vue在响应数据和衍生的绑定关系上也不一样。在Vue中,衍生只用三种,computed,watch以及render。而Mobx种提供了computed,autorun,when,reaction和observer。由于在Vue中响应式只是他的一部分,需要配合组件系统使用,所以数据和衍生的绑定是在内部实现的,可以理解成自动绑定依赖。Mobx提供了更加细粒度的调用,因此绑定的建立也更严格,需要满足:
- “读取” 是对象属性的间接引用,可以用过
.或者[]的形式完成。 - “追踪函数” 是
computed表达式、observer 组件的render()方法和when、reaction和autorun的第一个入参函数。 - “过程(during)” 意味着只追踪那些在函数执行时被读取的 observable 。这些值是否由追踪函数直接或间接使用并不重要。