引入 Mobx 使用和介绍
跟 Redux 相比
- 函数式 VS 面向对象
- redux 需要 connect,也需要 Immutable Data,reducer,action,文件、代码量较多,概念也多。 mobx 直接引用对象组织,修改数据。
- redux 数据流动很自然,任何 dispatch 都会导致广播,需要依据对象引用是否变化来控制更新粒度。mobx 数据流流动不自然,只有用到的数据才会引发绑定,局部精确更新,但免去了粒度控制烦恼。
- redux 有时间回溯,每个 action 都被记录下来,可预测性,定位错误的优势。mobx 只有一份数据引用,不会有历史记录。
- redux 引入中间件去解决异步操作,以及很多复杂的工作。mobx 没有中间件,数据改了就是改了,没有让你增加中间件的入口。
为什么用 mobx
- 简单,概念,代码少
- class 去定义、组织 store,数据、computed、action 定义到一块,结构更清晰,面向对象的思维更适合快速的业务开发
- 某个 store 的引用不一定非在组件中才能取到,因为是对象,可以直接引用。比如在 constant.js 文件中可以定义一些来自 store 的变量。
- 据说效率更高。mobx 会建立虚拟推导图 (virtual derivation graph),保证最少的推导依赖
mobx 概念
Observable state
给数据对象添加可观测的功能,支持任何数据结构。
class State { @observable price = 10; }
Computed values
某个 state 发生变化时,需要自动计算的值。比如说单价变化,总价的计算
class State { @observable price = 10; @observable count = 0; @computed get total() { return price * count; } }
Reactions
Reactions 和 Computed 类似,都是 state 变化后触发。但它不是去计算值,而是会产生副作用,比如 console、网络请求、react dom 更新等。mobx 提供了三个函数用于自定义 reactions。
const state = new State; autorun(() => { console.log("Current Price : " + state.price); })
每当 state 中的单价 price 发生变化,控制台都会打印。注意这里 state.count 变化是不会执行的。
react 组件 reactions。利用 mobx-react 中的 observer 对 react 组件进行包装。
import React, {Component} from 'react'; import {observer} from "mobx-react"; const View = (price, count) => <div> {price * count} </div> @observer class Container extends Component { render() { return ( <div> <Input /> <View price={state.price} count={state.count} /> </div> ) } }
Actions
个人觉得 mobx 中的 action 不像 redux 中是必需的,算是我们把一些修改 state 的操作都规范的用 action 标注,并且可以描述这个 action。随意的 state.price = 5
更改 state 都是可以起到作用的,只不过这样的话会很乱,你完全不知道哪里的操作引起了 state 的变化,所以 mobx 是建议你对 state 的副作用操作,都用 @aciton 去装饰。
class State { @observable price = 10; @observable count = 0; @computed get total() { return price * count; } @action priceChange(val) { this.price = val; } @action countChange(val) { this.count = val; } } const state = new State; class Input extends Component { handleChange = type => e => { const value = e.target.value; this.props[`${type}Change`](value); } render() { return ( <div> 价格:<input type='number' onChange={this.handleChange('price')} /> 数量:<input type='number' onChange={this.handleChange('count')} /> </div> ) } } const View = (total) => <div> {total} </div> @observer class Container extends Component { render() { const { total, priceChange, countChange } = state; return ( <div> <Input priceChange={priceChange} countChange={countChange} /> <View total={total} /> </div> ) } }
ps: 以上代码我都没跑过,有任何问题概不负责
异步 action
只需要把异步操作、请求也放到 @action 里就好了。假设我们已经封装好了一个 fetch 的方法,并返回一个 promise,现在去使用一个异步 action 请求 list 数据
@observable list = [ loading: false, dataSource: [], ]; @action getList = async () => { this.list.loading = true; const data = await fetch(); this.list.dataSource = data; }
常见问题
observable 之后的数组并不是普通数组的形式,所以有时候在组件内部做判断的时候可能会有问题,通常用 toJS() 转化一下。
observer 不要放到顶层 Page,因为当随便一个 state 的属性都改变,整个 Page 都会 render,即使其他 children 组件的 dom 结构没变,但还是会有一些性能开销的。所以 observer 尽量去包装小组件。
与自定义的 hoc 连用的时候,observer要 放到最里面。因为要包装组件的 render 函数,还要收集 state 的依赖。形如
@inject('store') @myHOC @observer class A extends Component { }
如果 observer 在外层,state 改变组件是不会做相应的。
不必像 redux 那样,把所有的 store 都注入 Provider, 虽然 mobx 支持这么做。一些公用的 store,比如用户信息啦,可以注入 Provider,然后子组件通过 inject 注入。但是大部分不能公用的列表数据,可以直接 import
import listStore from 'stores/list';
@observer class List extends Component {
render() {
return (
<ul>
{listStore.list.map(item => <li>{item.name}</li>)}
</ul>
)
}
}
mobx 和 react 的 state
使用了 mobx,你会发现 setState 那种写法以及不能立刻生效是很不习惯的。大部分情况下,你都可以不去写 state,而是通过 observable 去定义变量,observer 包装组件,这些变量也可以定义到组件内部(可观察的局部组件状态)。例如:
import {observer} from "mobx-react" import {observable} from "mobx" @observer class Count extends React.Component { @observable num = 0 componentDidMount() { setInterval(() => { this.num++ }, 1000) } render() { return <span>Count: { this.num } </span> } })
其他,参照官方文档中文链接
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: 目前使用的 Fork 工作流
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论