React 状态管理库: Mobx
原文地址:https://segmentfault.com/a/1190000010084073 React 是一个专注于视图层的库。React 维护了状态到视图的映射关系,开发者只需关心状态即可,由 React 来操控视图。 在小型应用中,单独使用 React 是没什么问题的。但在复杂应用中,容易碰到一些状态管理方面的问题,如:
这时就需要引入状态管理库。现在常用的状态管理库有 Mobx 和 Redux,本文会重点介绍 Mobx,然后会将 Mobx 和 Redux 进行对比,最后展望下未来的 React 状态管理方面趋势。 Mobx 简介 Mobx 的理念非常简单,可以用一个 demo 就把其核心原理说清楚。Mobx/MobxReact 中有三个核心概念,
简化图如下:
只讲概念还比较模糊,下面给大家举个例子。 点击运行 https://jsfiddle.net/jhwleo/1L5jcykr/9/ // 通过 observable 定义组件的状态
const user = mobx.observable({
name: "Jay",age: 22
})
// 通过 action 定义如何修改组件的状态
const changeName = mobx.action(name => user.name = name)
const changeAge = mobx.action(age => user.age = age)
// 通过 observer 定义 ReactComponent 组件。
const Hello = mobxReact.observer(class Hello extends React.Component {
componentDidMount(){
// 视图层通过事件触发 action
changeName('Wang') // render Wang
}
render() {
// 渲染
console.log('render',user.name);
return <div>Hello,{user.name}!</div>
}
})
ReactDOM.render(<Hello />,document.getElementById('mount')); // 非视图层事件触发,外部直接触发 action changeName('Wang2')// render Wang2 // 重点:没有触发重新渲染 // 原因:Hello 组件并没有用到 `user.age` 这个可观察数据 changeAge('18') // no console
例子看完了,是不是非常简单。 使用 Mobx,组件状态可以在外部定义(也可以在组件内部),因此,数据、业务逻辑可以轻易地和视图层分离,提高应用的可扩展性和可维护性。另外,由于组件状态可以在外部定义,兄弟节点之间的状态同步也非常容易。最后一点, Mobx 知道什么时候应该渲染页面,因此基本不需要手动设置 接下来给大家介绍下 Mobx 中 observableMobx 如此简单的原因之一,就是使用了可观察数据(Observable Data)。简单说,可观察数据就是可以观察到数据的读取、写入,并进行拦截。 Mobx 提供了 Array.isArray(mobx.observable([1,2,128);">3])) === false // true
mobx.isObservable(mobx.observable([true // true
注意,数组经过 虽然数据类型不一样,但是使用方式基本和原来一致(原始数据类型除外)。 const observableArr = mobx.observable([3]);
const observableObj = mobx.observable({name: 'Jay'});
const observableMap = mobx.observable(new Map([['name','Wang']]));
console.log(observableArr[0]) // 1
console.log(observableObj.name) // Jay
console.log(observableMap.get('name')) // Wang
可观察数据类型的原理是,在读取数据时,通过 Object.defineProperty(o,key,{
get : function(){
// 收集依赖的组件
return value;
},set : function(newValue){
// 通知依赖的组件更新
value = newValue
},});
在可观察数据被组件读取时,Mobx 会进行拦截,并记录该组件和可观察数据的依赖关系。在可观察数据被写入时,Mobx 也会进行拦截,并通知依赖它的组件重新渲染。 observer // 普通组件
const Hello = mobxReact.observer(class Component {
render() {
return </div>
}
})
// 函数组件
const Hello = mobxReact.observer( () => (
</div>
))
响应式组件,即当且仅当组件依赖的可观察数据发生改变时,组件才会自动响应,并重新渲染。 在本文最开始的例子中,响应式组件依赖了 这里再详细分析本文中的第一个例子: user.name = 'Wang2'// render Wang2
// 重点:没有触发重新渲染
// 原因:Hello 组件并没有用到 `user.age` 这个可观察数据
user.age = '18' // no console
当可观察数据变化时,Mobx 会调用 源码地址 而在传统 React 应用中,当状态、属性变化后会先调用 shouldComponentUpdate是很消耗性能的。Mobx 通过可观察数据,精确地知道组件是否需要更新,减少了调用 另外需要注意的是 action在 Mobx 中是可以直接修改可观察数据,来进行更新组件的,但不建议这样做。如果在任何地方都修改可观察数据,将导致页面状态难以管理。 所有对可观察数据地修改,都应该在 constchangeName = mobx.action(name=>user.name = name)
使用 Mobx 可以将组件状态定义在组件外部,这样,组件逻辑和组件视图便很容易分离,兄弟组件之间的状态也很容易同步。另外,也不再需要手动使用 Mobx 与 Redux 对比Mobx 的优势来源于可变数据(Mutable Data)和可观察数据(Observable Data) 。 Redux 的优势来源于不可变数据(Immutable data)。 可观察数据的优势,在前文已经介绍过了。现在再来聊聊可变数据和不可变数据。 顾名思义,可变数据和不可变数据的区别在于,可变数据创建后可以修改,不可变数据创建后不可以修改。 可变数据,可以直接修改,所以操作起来非常简单。这使得使用 mobx 改变状态,变得十分简单。 不可变数据并不一定要用到 Immutable 库。它完全可以是一种约定,只要创建后不修改即可。比如说,Redux 中的 reducer(state,action) => newState. 不可变数据的优势在于,它可预测,可回溯。示例代码如下: function foo(bar) {
let data = { key: 'value' };
bar(data);
console.log(data.key); // 猜猜会打印什么?
}
如果是可变数据, Mobx 与 Redux 技术选型的本质,是在可变数据与不可变数据之间选择。具体业务场景的技术选型,还需要根据实际情况进行分析,脱离业务场景讨论技术选型是没有意义的。但我个人在状态管理的技术选型上,还是倾向于 Mobx 的。原因是前端与副作用打交道非常频繁,有 Http 请求的副作用,Dom 操作的副作用等等。使用不可变数据,还必须得使用中间件对副作用封装;在 Redux 中修改一次状态,需要经过 Action、Dispatch、Reducer 三个步骤,代码写起来太啰嗦;而前端的程序以中小型程序为主,纯函数带来的可预测性的收益,远不及其带的代码复杂度所需要付出的成本。而 Mobx 使用起来更加简单,更适合现在以业务驱动、快速迭代的开发节奏。 展望:Mobx 与不可变数据的融合不可变数据和可变数据,都是对状态的一种描述。那么有没有一种方案,能将一种状态,同时用可变数据和不可变数据来描述呢?这样就可以同时享有二者的优势了。(注意:当我们说可变数据时,通常它还是可观察数据,后文统一只说可变数据。) 答案是肯定的,它就是 MST(mobx-state-tree)https://github.com/mobxjs/mob...。 MST 是一个状态容器:一种状态,同时包含了可变数据、不可变数据两种不同的形式。 为了让状态可以在可变数据和不可变数据两种形式之间能够高效地相互转化,必须遵循 MST 定义状态的方法。 在 MST 中,定义状态必须先定义它的结构。状态的结构是一颗树(tree),树是由多层模型(model)组成,model 是由多个节点组成。 在下面的代码中,树只有一层 model,该 model 也只有一个节点:title。title 的类型是事先定好的,在这里是 import {types} from "mobx-state-tree"
// declaring the shape of a node with the type `Todo`
const Todo = types.model({
title: types.string
})
// creating a tree based on the "Todo" type,with initial data:
const coffeeTodo = Todo.create({
title: "Get coffee"
})
在一些稍微复杂的例子中,树的 model 可以有多层,每层可以有多个节点,有些节点定义的是数据类型( import { types,onSnapshot } from "mobx-state-tree"
const Todo = types.model("Todo",{
title: types.string,done: false
},{
toggle() {
this.done = !this.done
}
})
const Store = types.model("Store",{
todos: types.array(Todo)
})
// create an instance from a snapshot
const store = Store.create({ todos: [{
title: "Get coffee"
}]})
最关键的来了,请看下面的代码。 // listen to new snapshots
onSnapshot(store,(snapshot) => {
console.dir(snapshot)
})
// invoke action that modifies the tree
store.todos[0].toggle()
// prints: `{ todos: [{ title: "Get coffee",done: true }]}`
在上述代码的第一部分,使用 MST 这么神奇,那么具体怎么用呢?MST 只是一个状态容器,同时包含了可变数据和不可变数据。你可以用 MST 直接搭配 React 使用。可以 MST + Mobx + React 配合着用,还可以 MST + Redux + React 混搭着用。 MST 比较新,业内的实践非常少,如果不是急需,现在还可以先观望一下。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |