核心概念
可观察状态(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 示例: https://github.com/mobxjs/mobx-react-boilerplate
深入理解 Mobx:
Mobx 官方文档: https://cn.mobx.js.org/
来源: https://juejin.im/post/5c8a8158e51d453196293cf5