使用 react 写小型应用,数据、业务逻辑和视图的模块划分不是很细是没有问题的。在这个阶段,引入任何状态管理库,都算是奢侈的。可是随着页面逻辑的复杂度提高,在中大型应用中,数据、业务逻辑和视图,若是不能很好的划分,就颇有可能出现维护难、性能低下的问题。javascript
业内比较成熟的解决方案有 redux,可是 redux 使用过程当中,给个人感受是太复杂和繁琐。那么为何不简单一点呢?mobx 的核心理念是 简单、可扩展的状态管理库。这可能正是你想要的。java
react 关注的状态(state)到视图(view)的问题。而 mobx 关注的是状态仓库(store)到的状态(state)的问题。react
mobx 最最核心的概念只有2个。 @observable
和 @observer
,它们分别对应的是被观察者和观察者。这是你们常见的观察者模式,不过这里使用了,ES7 中的 装饰器。es6
使用 @observable
能够观察类的值。redux
这里使用 @observable
将 Store 的 todos
变为一个被观察的值。浏览器
仓库缓存
// 这里引入的是 mobx import {observable} from 'mobx'; class Store { @observable todos = [{ title: "todo标题", done: false, }]; }
mobx 组件dom
而后再使用 @observer
,将组件变为观察者,响应 todos
状态变化。
当状态变化时,组件也会作相应的更新。函数
// 这里引入的是 mobx-react import {observer} from 'mobx-react'; @observer class TodoBox extends Component { render() { return ( <ul> {this.props.store.todos.map(todo => <li>{todo.title}</li>)} </ul> ) } }
完整的 demo 以下。性能
import React, {Component} from 'react'; import { render } from 'react-dom'; import {observable} from 'mobx'; import {observer} from 'mobx-react'; // 最简单的 mobx 就是一个观察者模式 class Store { // 被观察者 @observable todos = [{ title: "完成 Mobx 翻译", done: false, }]; } // 观察者 @observer class TodoBox extends Component { render() { return ( <ul> {this.props.store.todos.map(todo => <li>{todo.title}</li>)} </ul> ) } } const store = new Store(); render( <TodoBox store={store} />, document.getElementById('root') );
经过以上的简单的例子,展示了 mobx 分离数据、视图的能力。
这一小节要介绍的两个概念虽然也是核心概念,可是是可选的。
前面例子,只讲了状态的读取,那么状态应该如何写入呢?
答案是直接写入!
@observer class TodoBox extends Component { render() { return ( <div> <ul> {this.props.store.todos.map( (todo,index) => <li key={index}>{todo.title}</li> )} </ul> <div> <input type="button" onClick={() => { // 直接修改仓库中的状态值 this.props.store.todos[0].title = "修改后的todo标题" }} value="点我"/> </div> </div> ) } }
细心的朋友必定发现了奇怪的地方,react 官方说过 props
值不能直接修改,可是引入 mobx 后 props
能够直接修改了,这太奇怪了!
解决办法就是 mobx 的下一个概念 action
。
首先在 Store 中,定义一个 action。
class Store { @observable todos = [{ title: "todo标题", done: false, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } }
在 Component 中调用,这样经过 action 的方法,就避免了直接修改 props
的问题。
<input type="button" onClick={() => { this.props.store.changeTodoTitle({index:0,title:"修改后的todo标题"}); }} value="点我"/>
能够经过引入 mobx 定义的严格模式,强制使用 action 来修改状态。
import {useStrict} from 'mobx'; useStrict(true);
在有些时候,state 并不必定是咱们须要的最终数据。例如,全部的 todo 都放在 store.todos 中,而已经完成的 todos 的值(store.unfinishedTodos),能够由 store.todos 衍生而来。
对此,mobx 提供了 computed
装饰器,用于获取由基础 state 衍生出来的值。若是基础值没有变,获取衍生值时就会走缓存,这样就不会引发虚拟 DOM 的从新渲染。
经过 @computed
+ getter
函数来定义衍生值(computed values)。
import { computed } from 'mobx'; class Store { @observable todos = [{ title: "todo标题", done: false, },{ title: "已经完成 todo 的标题", done: true, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } @computed get finishedTodos () { return this.todos.filter((todo) => todo.done) } }
mobx 有一套机制,若是衍生值(computed values)所依赖的基础状态(state)没有发生改变,获取衍生值时,不会从新计算,而是走的缓存。所以 mobx 不会引发过分渲染,从而保障了性能。
当渲染的值为 finishedTodos ,点击修改标题,不会在控制台打印 "render";
换成 todos,就会打印 "render".
这是因为已完成的 todos 值没有改变,因此不会从新计算,而是走的缓存。所以不会调用 render 方法。
完整 demo 以下
import React, {Component} from 'react'; import { render } from 'react-dom'; import {observable, action, computed,useStrict} from 'mobx'; import {observer} from 'mobx-react'; useStrict(true); class Store { @observable todos = [{ title: "todo标题", done: false, },{ title: "已经完成 todo 的标题", done: true, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } @computed get unfinishedTodos () { return this.todos.filter((todo) => todo.done) } } @observer class TodoBox extends Component { render() { console.log('render'); return ( <div> <ul> { /* 把 unfinishedTodos 换成 todos,点击修改标题就会在控制台打印 "render".*/ } {this.props.store.unfinishedTodos.map( (todo,index) => <li key={index}>{todo.title}</li> )} </ul> <div> <input type="button" onClick={() => { this.props.store.changeTodoTitle({index:0,title:"修改后的todo标题"}); }} value="修改标题"/> </div> </div> ) } } const store = new Store(); render( <TodoBox store={store} />, document.getElementById('root') );
翻译了官网的一段文章,就拿过来作小结了。
mobx 是一个的简单、可扩展的状态管理库。它背后的哲学很是简单:
应用程序 state 是最基础的数据。任何能够从 state 中衍生出来的数据,都应该自动的被衍生出。
actions 是惟一可以改变 state 的方法。
state 是最基础的数据,它不该该包含冗余的和派生的数据。
computed values 派生值是经过纯函数从 state 中派生而来的。当派生值依赖的状态发生变化了,Mobx 将会自动更新派生值。若是依赖的状态没有改变,mobx 会作优化处理。
reactions 也是派生数据,是从 state 中派生而来的。它的反作用是自动更新 UI。(注:mobx 有一个 reaction 接口,当 state 改变时,就会调用它的回调。UI 是经过 reaction 更新的。)
React 和 MobX 是很是强大的组合。React 提供了将应用状态映射为可渲染的组件树的机制。MobX 提供存储和更新应用状态的机制,供 React 使用。
React 和 MobX 提供了开发过程当中常见问题的解决方案。 React 经过使用虚拟 DOM,减小了对浏览器 DOM 的操做。MobX 经过使用了响应式虚拟依赖状态图(reactive virtual dependency state graph) ,提供了应用程序状态与 React 组件同步的机制,这样 state 只会在须要时更新才会更新。(译者注:这段有点难理解,大概的意思是 Mobx 关注的是 store 到 state 的过程,React 关注的是 state 到 view 的过程)。
在实际开发中,须要用到很多 mobx 的辅助函数,这些辅助函数一共 14 个,挑了一些列举以下。
autorun
observable 的值初始化或改变时,自动运行。
trasaction
批量改变时,经过 trasaction 包装,只会触发一次 autorun。
extendsObservable
对类的属性或实例,进行监听。
observable
对普通对象进行监听。
map
使用 asMap 将对象转化为 map。
action-strict
在 mobx.usrStrict(true)时,只能经过 action 触发值的改变。
when
相似 autorun.
mobx.when 第一个参数是一个函数,初始化时也会自动执行。该函数返回一个 boolean 值,当返回值为 true 的时候,才会继续触发第一个函数。当返回值为 flase 时,再也不继续监听。这时会执行 mobx.when 的第二个参数,这个参数也是一个函数。
reaction
相似 autorun.
reaction 不会在初始化时执行,只会在值改变的时候执行。
该函数有 2 个值,第一个参数是一个函数,返回监听的值.
第二个参数,也是一个函数,会在值改变的时候执行。
spy
相似 aoturun.
监听全部 mobx 的事件。
包含一个 type ,该值用来区分执行事件的类型。
whyRun用于调试,打印 autorun 为何会触发。