Mobx相信大家都听过,也有部分同学用过,在状态管理界,和Redux同日争辉。在本文中,将通过介绍Mobx的核心概念、设计原则及问答形式的方式,带大家走进Mobx的世界,一窥其中的原理。原文地址:leeing.site/2019/03/15/…
核心概念
可观察状态(Observable state)
状态是驱动应用的数据。通常有像待办事项列表这样的领域特定状态,还有像当前已选元素的视图状态。记住,状态就像是有数据的excel表格。
衍生(Derivations)
任何源自状态并且不会再有任何进一步的相互作用的东西就是衍生。衍生以多种形式存在
- 用户界面
- 衍生数据,比如剩下的待办事项的数量
- 后端集成,比如把变化发送到服务器端
Mobx区分了两种类型的衍生
- 计算值(Computed values),它们是永远可以用纯函数从当前可观察状态中衍生出的值
- 反应(Reactions),它们是当状态改变时需要自动发生的副作用,用来连接命令式编程和响应式编程,最终都需要实现I/O操作,例如发送请求,更新页面等。
动作(Actions)
动作是任意一段可以改变状态的代码。用户事件,后端数据推送等。
Mobx中可以显示地定义动作,它可以帮你把代码组织的更清晰。严格模式下,Mobx强制要求只有Action可以修改状态。
设计原则
Mobx支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的图。
Action —> State —> Views
当状态改变时,所有衍生都会进行原子级的自动更新。因此永远不可能观察到中间值。
所有衍生默认都是同步更新的。这意味着动作可以在改变状态之后直接可以安全地检查计算值。
计算值是延迟更新的。任何不在使用状态的计算值将不会更新,直到需要它进行副作用(I/O)操作时。如果视图不再使用,那么它会自动被垃圾回收。
所有的计算值都应该是纯净的。它们不应该用来改变状态。
原理
重要思想:在运行时才能实现最小、一致地订阅子集
问:Mobx如何有效地将所有衍生保持在一个一致地状态? 答:不缓存数据,在需要时重新计算。Mobx认为这是很高效地,因为Mobx不会计算所有衍生,只会计算确保参与反应的计算。这被称为响应式地。
问:没有参与反应的衍生呢? 答:如果一个衍生没有被激活,它将被按需处理。就像一个普通的getter函数一样,懒衍生如果没有用了,将被简单的垃圾回收。所有computed需要使用纯函数,因为对于纯函数而言,是懒衍生还是直接使用并不重要,在相同的状态下,总是返回相同的结果。
问:当状态变化时,衍生是如何计算的? 答:当重新计算被触发时,衍生函数将被压入到衍生堆栈中。只要计算正在运行,每个被访问的状态都会将自身注册为衍生堆栈最顶层函数的依赖项。当计算值被需要了,如果该值已经处于reactive状态,则该值可以简单最后已知的值,否则它将push自己到衍生堆栈中,切换到reactive模式并开始计算,具体计算过程如下:
-
可观察值像所有观察者发送过时通知,表明它已经变得陈旧。任何受影响的衍生将以递归的 方式将通知传递给其观察者。因此,依赖关系树的一部分将被标记为陈旧。
-
在发送陈旧通知并存储新值后,一个就绪通知将被发送,用于指示该值是否确实发生了变化
-
一旦衍生收到步骤1中每个陈旧通知的就绪通知,它就会知道所有的被观察值都稳定了,于是将 开始重新计算。计算就绪和陈旧消息的数量可以确保这一点。
-
如果没有就绪通知指出一个值变化了,衍生将直接告诉自己的观察者它已经准备好了且没有变 化中的值
同步执行 Mobx同步运行所有内容。这有2大好处:
- 不可能观察陈旧的衍生
- 追踪堆栈和调试变得简单
Mobx还提供事务机制。事务推迟所有就绪通知,直到事务块执行完成后,同步运行和更新所有内容。
对比Redux
- Redux将数据保存在单一store中,Mobx将数据保存在分散的多个store中
- Redux需要手动处理变化后的操作,Mobx使用observable保存数据,数据变化后自动处理响应的操作
- Redux使用不可变状态,不能直接去修改它,而是应该使用纯函数返回一个新的状态;Mobx中的状态是可以直接修改的
参考链接
- demo示例:github.com/mobxjs/mobx…
- 深入理解Mobx:hackernoon.com/becoming-fu…
- Mobx官方文档:cn.mobx.js.org/