快速入门 MboX
一:MboX 基本概念
介绍:
- 简单、可扩展( ?)的状态管理工具;
- 运用透明式的 函数式响应编程 ;
mobx 与 redux 对比:
| redux | mobx |
|---|---|
| 严格的工作流程,冗余的模板代码 | 代码简洁 |
| 保证数据不可变 | 数据响应式,可直接修改数据(Proxy) |
| 中间件处理异步 | 可直接处理异步 |
| redux 约束强,适合大型多人协作开发 | 适合简单项目 |
mobx 版本说明:
- V4 可运行在任何支持 ES5 语法的 浏览器(
Object.defineProperty); - V5 可运行在 ES6 语法的 浏览器(
Proxy) - V4、V5具有相同 API ,可使用 装饰器 语法;
V6 最新版本,默认放弃 装饰器语法(可配置修改支持装饰器语法);
二:MboX 基本使用
2.1 环境配置:
使用
create-react-app初始化项目,npx create-react-app project-name --template typescript;- 安装
mobx/mobx-react(mobx-react-lite只支持函数组件);
2.2 核心概念:
observable定义一个可存储 state 的可追踪字段;action将一个方法标记为修改 state 的 action;computed标记一个可由 state 派生出新值并且 缓存 其输出的计算属性;- 工作流程:

2.3 创建 Store:
- 新建文件夹 store,创建文件
counter.ts,通过 class 创建一个 Counter 类; - 使用
makeObservable将类的 属性、方法 变为响应式; - 导出
Counter类; - 注意:mobx 中的每一个 store 都只初始化一次(mobx 中可有多个 store。如何保证每个 store 只初始化一次?)。 ```typescript import { makeObservable, observable, action } from ‘mobx’
class Count { constructor() { / 参数1:target 指定对象变成可观察的; 参数2:指定哪些 属性、方法变为可观察的; / makeObservable(this, { num: observable, setIncrement: action.bound, setDecrement: action.bound, setReset: action.bound }) } num = 0 setIncrement() { this.num++ } setDecrement() { this.num— } setReset() { this.num = 0 }
// 计算属性 get doubleNum() { console.log(‘执行几次。。。。’); return this.num * 2 } }
// 如何保证每个 store 只初始化一次?导出之前实例化一次后,再导出,即可满足。 const countStore = new Count()
export default countStore
```typescriptimport { observer } from 'mobx-react'import Counter from './store/counter'function App() {// ...}// observer 是个高阶函数,包裹组件,此时 Counter 内的属性改变时,才会精准更新这个组件export default observer(App)
2.4 **this**指向问题:
默认 class中的方法不会绑定this,this指向取决于如何调用。<button onClick={() => countStore.setIncrement()}>+</button>
此时setIncrement方法在 箭头函数 中,被countStore调用,此时setIncrement方法中的this指向countStore对象,指向没问题。
如改为如下:<button onClick={countStore.setIncrement}>+</button>
此时是将setIncrement方法赋值给 onClick 事件,当onClick 调用setIncrement方法时,此方法中的this指向undefined。
如何解决:在使用makeObservable时,可通过action.bound绑定this指向。
makeObservable(this, {num: observable,setIncrement: action.bound, // 重点,通过 bound 将 setIncrement 方法绑定到实例对象上,不可变。setDecrement: action.bound,setReset: action.bound})
此时在组件中调用:<button onClick={countStore.setIncrement}>+</button>就没有问题。
2.5 计算属性:
computed能从可观察对象中派生出信息;- 惰性求值,会缓存其输出;
- 计算属性在类中是一个方法,且方法前面必须使用
get修饰; - 也可在
makeObservale中指定方法是计算属性。 ```typescript // 法一: makeObservable(this, { num: observable, setIncrement: action.bound, // 重点,通过 bound 将 setIncrement 方法绑定到实例对象上,不可变。 setDecrement: action.bound, setReset: action.bound, double: computed })
// 法二,类比 Vue 中的计算属性 get double() { return this.a + this.b }
**2.6 **`**makeAutoObservable**````typescriptconstructor() {makeAutoObservable(this, { 排除不需要被观察的属性、方法 }, { autoBind: true })}
makeAutoObservable是加强版的makeObservable,默认情况下它将推断出所有属性:
推断规则如下:
autorun函数接收一个函数 A 作为参数,每当该函数中观察的属性值发生改变时,A 就会执行。autorun函数,创建时,会执行一次;- MobX 会自动收集并订阅所有的可观察属性,一旦发生变化,
autoRun就会触发。 ```typescript class Counter{}
const counter = new Counter()
autorun(() => { console.log(counter.num) })
**3.2 **`**reaction**`**(方法二)**- `reaction`类似`autorun`,但可更精细控制要跟踪的可观察对象;- `reaction`接收两个参数:- 参数1:data 函数,其返回值将会作为第二个函数输入;- 参数2:callback;- `reaction`与`autorun`不同,`reaction`在初始化时不会自动执行。```typescriptreaction(() => counter.num, (v, oldV) => {console.log('counter.doubleNum', v, oldV)})
四:MboX 处理异步
4.1 MobX 如何处理异步
- 异步进程在 mobx 中不需要特殊处理,因为不论何时引发的所有 reactions 都将会自动更新;
- 可观察对象是可变的,因此在 action 中保持对它们的引用一般是安全的;
- 若可观察对象的修改不在 action 函数中,控制台会报错(报错可关闭,但是不推荐)。
- 可通过从
import { configure } from 'mobx',然后配置:configure({ enforceActions: 'never' }),此时enforceActions: 'observed' | 'never'。 - observed:可观察状态必须在过 action 修饰过的函数中来修改;
- never:可观察状态可在任何地方修改(不推荐)。 ```typescript import { makeObservable, observable, action } from ‘mobx’
- 可通过从
class Count { constructor() { makeObservable(this, { num: observable, setIncrement: action.bound, }) } num = 0
/ setIncrement() { // 此时修改的 num 并不是在 action 包裹的函数,而是在定时器的 fun 中修改的,此时,控制台就会出现警告 setTimeout(() => { this.num++ }, wait) } / // 处理异步函数,方式一,如下两个方法配合使用 setIncrement() { this.num++ } // 重点 setIncrementAsync() { setTimeout(this.setIncrement, wait) } }
// 如何保证每个 store 只初始化一次?导出之前实例化一次后,再导出,即可满足。 const countStore = new Count()
export default countStore
**4.2 **`**runInAction**`**处理异步**- 通过`runInAction`可保证所有异步更新可观察对象的步骤都应标识为 action;```typescriptimport { makeObservable, observable, action, runInAction } from 'mobx'class Count {constructor() {makeObservable(this, {num: observable,setIncrement: action.bound,})}num = 0/*setIncrement() {// 此时修改的 num 并不是在 action 包裹的函数,而是在定时器的 fun 中修改的,此时,控制台就会出现警告setTimeout(() => {this.num++}, wait)}*/// 处理异步函数,方式二setIncrement() {setTimeout(() => {runInAction(() => { // 重点,this.num++})}, wait)}}// 如何保证每个 store 只初始化一次?导出之前实例化一次后,再导出,即可满足。const countStore = new Count()export default countStore
五:MboX 模块化
5.1 多个 store 的场景
出现多个 Store,通过一个根 Store 统一管理所有的 Store,同时还可以相互调用,最后使用 useContext自定义 Hook (useStore)统一导出 Store。
类比:redux 只有一个 Store。
import { createContext, useContext } from 'react'import XX from 'store/xx' // 各个子 storeclass RootStore {constructor() {this.xx1 = XX1this.xx2 = XX2// 也可以在引入各个子类Store 之后,再实例化,也可各个子类之间相互共享、传递数据。/*this.xx1 = new XX1()this.xx2 = new XX2()*/}}const store = new RootStore()const context = createContext(store)export default function useStore () {return useContext(context)}
在组件中使用时:const { xx1, xx2 } = useStore()
