mobx入门教程
背景
React的自身的状态本身由state和setState维护;但是随着应用复杂度的提升(组件间状态共享及状态变更),单纯通过setState进行状态管理的方案不仅变得代码复杂、难以维护,而且影响可扩展性;
针对这种状况,React目前主要有Redux和mobx两种解决方案(均可以让状态逻逻辑从展示组件等解耦出来,提高扩展性与复用性);
mobx与Redux有什么不同?
-
Redux采用FP原则,核心为action、store、reducer,采用单一数据源,状态不可变更(总是返回一个新状态),使用纯函数进行状态变更;Redux更新时总是会返回一个新的state(redux底层的combineReducers会通过比较对象的引用来判断是否同一个对象,如果是的话则继续使用旧的state,即不会有任何UI更新,否则执行更新操作)为什么redux要求不可变性 ?- 浅比较:redux的
combineReducers及react-redux的connect方法
生成的组件在比较根组件state与mapStateToProps函数返回值以确定是否更新时,都用到了浅比较;浅比较的前提是数据不可变; -
不可变数据的管理极大地提升了数据处理的安全性。
-
进行时间旅行调试要求 reducer 是一个没有副作用的纯函数,以此在不同 state 之间正确的移动。
什么是时间旅行?在 2015 年的 React Europe 会议上,Dan Abramov 展示了通过 Redux DevTools 让开发者在历史状态中自由穿梭, 提升调试体验
时间旅行主要用于事件驱动的应用,比如富文本编辑器的状态回撤、游戏进度的读取和保存等等;具体参考从时间旅行的乌托邦,看状态管理的设计误区(state, action) => newState - 浅比较:redux的
-
mobx采用OOP原则,也是响应式编程;采用observable和observer的概念,observable中将对象变为被观察对象,可以通过action等变更状态;observer定义观察对象,可以观察被观察对象的变更并应用;mobx中状态是可变的,更加轻量级,入门比Redux简单;需要注意的是,MobX 在重绘时的性能优势是以访问劫持后更大的内存占用为代价的;(开箱即用的mobx与优化后的redux性能相当)
-
要点比较


Mobx概述
mobx支持单向数据流,由原始状态衍生的值都会自动变更,无法观察到中间值;
- 创建应用模型,定义状态使其可观察(建立store,即被观察对象)
- 创建视图以响应状态的变化(创建观察者,检测并响应状态变化)
- 更改状态
环境配置
mobx使用装饰器特性的话需要开启ES7的decorator属性;可以安装插件@babel/plugin-proposal-decorators"在package.json中添加以下配置:
"babel": {"plugins": [["@babel/plugin-proposal-decorators",{"legacy": true}]],"presets": ["react-app"]}
否则,可以借助mobx API —— decorate(object, decorators)实现部分功能
Mobx API —— observable相关
-
描述:observable用于创建被观察对象(定义可观察状态),需要注意的是被观察对象总是类的属性而不是值;
-
用法:
// 用法一, 会自动递归到整个对象 observable(value)// 用法二 是extendObservable(this, { property: value})的语法糖 @observable classProperty = value// demo import { observable, computed } from "mobx";class Order {@observable price = 0;@observable count = 1;@computed get total() {return this.price * this.count;}@action.boundincreasePriice() {this.price++;} }// demo2 —— 使用decorate重写demo import { observable, computed, decorate } from 'mobx';class Order {price = 0;count = 1;get total() {return this.price * this.count;}increasePriice() {this.price++;} }decorate(Order, {price: observable,count: observable,total: computed,increasePrice: action.bound })
Mobx API —— 对observable做出响应
1. computed——计算值
-
含义:computed可以根据现有的状态或其他计算值衍生出新的值,并且这些值可以被observer使用;
autorun主要适用于产生一个副作用而不是一个新值,如日志打印和网络请求等; -
要点:
computed是响应式的,如果前一个依赖计算的值未发生变化,计算属性不会重新运行,这种情况下它会被暂停(自动暂停特性)。同时,如果一个计算属性不再被观察,其将会自动进行垃圾回收。不要把 computed 和 autorun 搞混。它们都是响应式调用的表达式,但是,如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed,如果你不想产生一个新值,而想要达到一个效果,请使用 autorun。 举例来说,效果是像打印日志、发起网络请求等这样命令式的副作用。
-
用法:
// 用法一:函数形式 computed(expression) // demo import {observable, computed} from "mobx"; var name = observable.box("John");var upperCaseName = computed(() =>name.get().toUpperCase() );var disposer = upperCaseName.observe(change => console.log(change.newValue));name.set("Dave"); // 输出: 'DAVE'// 用法二:装饰器。在任意属性的getter上使用 @computed get propertyName() {return }
2. autorun——自定义反应
-
含义:如果创建一个函数,本身不会有观察者,并且其不产生新值而是用于产生一个副作用(网络请求/日志打印/更新UI)等时,需要使用
autorun; -
要点:
computed(function) 创建的函数只有当它有自己的观察者时才会重新计算,否则它的值会被认为是不相关的。而autorun创建后会立即运行一次; -
用法
第一个参数一般为函数(如果是字符串,将被用作调试名),第二个参数为可选的对象参数,包括属性:delay(去抖时间)/name(reaction名称)/onError(处理reaction错误)/scheduler(设置自定义调度器决定autorun函数如何重新运行)autorun(() => {xxx }, { delay: 200 })
3. reaction——自定义反应
-
含义:可以将
reaction看作是autorun的变种,为`observable的追踪提供了更加细粒度的控制; -
用法:
第一个为数据追踪函数,第二个为副作用函数(其中的任何observable都不会被追踪);第三个为可选的对象参数;
只用在数据追踪函数返回新值时副作用函数才会运行;reaction(() => data, (data, reaction) => { sideEffect }, options?) -
demo:
// 只调用一次并清理掉 reaction : 对 observable 值作出反应。 const reaction3 = reaction(() => counter.count,(count, reaction) => {console.log("reaction 3: invoked. counter.count = " + count);reaction.dispose();} );counter.count = 1; // 输出: // reaction 3: invoked. counter.count = 1counter.count = 2; // 输出: // (There are no logging, because of reaction disposed. But, counter continue reaction)
4. @observer——函数装饰器
-
含义:可以将React组件转为响应式组件,响应被观察对象的改变;
-
要点:它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件
-
用法:
当 observer 需要组合其它装饰器或高阶组件时,请确保 observer 是最深处(第一个应用)的装饰器,否则它可能什么都不做。
observer(class Demo ... { })@observer class Demo extends React.Component {... }const Demo = observer(({ data }) =><span>type: { data.type } </span> );
5. 使用 inject 将组件连接到提供的 stores
import { observer, inject} from 'mobx-react';
import React from 'react';@inject('store')
@observer
class Casual extends React.Component {render() {return (<div><h2>num: {this.props.store.num}</h2><button onClick={this.handleClick}>increment</button></div>)}handleClick = () => {this.props.store.increment();}
}export default Casual;// store.js
import { action, observable, computed, decorate } from 'mobx';class Store {@observable num = 0;@computed get retNum() {return `the value of num is ${this.num}`} @action.boundincrement() {this.num++;}
}export default Store;
6. componentWillReact
-
含义:当组件因为它观察的数据发生重新渲染时,会触发该生命周期函数;可以追溯渲染并找到导致渲染的操作
-
要点:
componentWillReact不接受任何参数;初始化渲染时不会被触发;@observer 以和 PureComponent 同样的方式实现了 shouldComponentUpdate,因此子组件可以避免不必要的重新渲染
-
demo:
import {observer} from "mobx-react";@observer class TodoView extends React.Component {componentWillReact() {console.log("I will re-render, since the todo has changed!");}render() {return <div>this.props.todo.title</div>;} }
Mobx API —— 改变observables
1. action
-
含义:用于修改状态;
-
用法
action(fn) action(name, fn) @action classMethod() {} @action(name) classMethod() {} // @action.bound可以自动绑定到目标对象(即指定了this作用域) @action.bound classMethod() {} ... -
action.bound不要和箭头函数一起使用;
参考文献
- mobx中文文档
- Ten minute introduction to MobX and React
- Redux vs MobX without Confusion
- Becoming fully reactive: an in-depth explanation of MobX
- 为什么 Redux 需要不变性?
- Redux 是如何使用浅比较的?
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
