在正式进入主题前,你须要确认一下,是否已经掌握下面几个工具和库的使用:git
MobX
的Observable
React
来测试MST的功能很是简单上面列举的工具和库都有很是丰富的文档和教程,不太熟悉的同窗最好先自学一下。github
MST依赖MobX。api
项目中执行yarn add mobx mobx-state-tree
便可完成安装。promise
MobX有两个版本,新版本须要浏览器Proxy支持,一些老旧的浏览器并不支持,须要兼容老浏览器的请安装mobx@4:yarn add mobx@4 mobx-state-tree
。浏览器
使用MST来维护状态,首先须要让MST知道,这个状态的结构是什么样的。缓存
MST内建了一个类型机制。经过类型的组合就能够定义出整个状态的形状。安全
而且,在开发环境下,MST能够经过这个定义好的形状,来判断状态的值和形状与其对应的类型是否匹配,确保状态的类型与预期一致,这有助于在开发时及时发现数据类型的问题:数据结构
MST提供的一个重要对象就是types
,在这个对象中,包含了基础的元类型
(primitives types),如string
、boolean
、number
,还包含了一些复杂类型的工厂方法和工具方法,经常使用的有model
、array
、map
、optional
等。app
model
是一个types
中最重要的一个type,使用types.model
方法获得的就是Model
,在Model
中,能够包含多个type或者其余Model
。异步
一个Model
能够看做是一个节点(Node),节点之间相互组合,就构造出了整棵状态树(State Tree)。
MST可用的类型和类型方法很是多,这里不一一列举,能够在这里查看完整的列表。
完成Model
的定义后,可使用Model.create
方法得到Model
的实例。Model.create
能够传入两个参数,第一个是Model
的初始状态值,第二个参数是可选参数,表示须要给Model
及子Model
的env对象(环境配置对象),env用于实现简单的依赖注入
功能,在后续文章中再详细说明。
props指的是Model
中的属性定义。props定义了这个Model
维护的状态对象包含哪些字段,各字段对应的又是什么类型。
拿开篇中的“商品”做为例子:
import { types } from 'mobx-state-tree';
export const ProductItem = types.model('ProductItem', {
prodName: types.string,
price: types.number,
});
复制代码
types.model
方法的第一个参数为Model
设定了名称,第二个参数传入了一个对象,这个对象就是Model
的props。
上面代码中,指定了ProductItem
这个Model
包含了类型为string
的prodName
属性和类型为number
的price
属性。
注意,能够省略types.model
的第二个参数,而后使用model.props
方法来定义props。
export const ProductItem = types
.model('ProductItem')
.props({
prodName: types.string,
price: types.number,
});
复制代码
上面的两份代码获得的ProductItem
是相同的(实际上有一些细微差异,但能够彻底忽略)。
定义了props以后,在Model的实例上能够访问到相应的字段:
const productItem = ProductItem.create({prodName: '商品标题xxx', price: 99.9});
console.log(productItem.prodName); // 商品标题xxx
console.log(productItem.price); // 99.9
复制代码
views是Model
中一系列衍生数据
或获取衍生数据的方法
的集合,相似Vue组件的computed计算属性。
在定义Model
时,可使用model.views
方法定义views。
export const ProductItem = types
.model('ProductItem', {
prodName: types.string,
price: types.number,
discount: types.number,
})
.views(self => ({
get priceAfterDiscount () {
return self.price - self.discount;
}
}));
复制代码
上面代码中,定义了priceAfterDiscount
,表示商品的折后价格。调用.views
方法时,传入的是一个方法,方法的参数self
是当前Model
的实例,方法须要返回一个对象,表示Model
的views集合。
须要注意的是,定义views时有两种选择,使用getter
或者不使用。使用getter
时,衍生数据的值会被缓存直到依赖的数据发送变化。而不使用时,须要经过方法调用的方式获取衍生数据,没法对计算结果进行缓存。尽量使用getter
,有助于提高应用的性能。
actions是用于更新状态的方法集合。
在建立Model
时,使用model.actions
方法来定义actions:
const Root = types
.model('Root', {
str: types.string,
})
.actions(self => ({
setStr (val: string) {
self.str = val;
}
}));
const root = Root.create({str: 'mobx'});
root.setStr('mst');
复制代码
在安全模式下,全部对状态的更新操做必须在actions中执行,不然会报错:
可使用unprotect
方法解除安全模式(不推荐):
import { types, unprotect } from 'mobx-state-tree';
const Root = types.model(...);
unprotect(Root);
root.str = 'mst'; // ok
复制代码
除了一般意义上用来更新状态的actions外,在model.actions
方法中,还能够设置一些特殊的actions:
从名字上能够看出来,上面四位都是生命周期方法
,可使用他们在Model
的各个生命周期执行一些操做:
const Model = types
.model(...)
.actions(self => ({
afterCreate () {
// 执行一些初始化操做
}
}));
复制代码
具体的MST生命周期在后续文章中再详细讨论。
异步更新状态是很是常见的需求,MST从底层支持异步action。
const model = types
.model(...)
.actions(self => ({
// async/await
async getData () {
try {
const data = await api.getData();
...
} catch (err) {
...
}
...
},
// promise
updateData () {
return api.updateData()
.then(...)
.catch(...);
}
}));
复制代码
须要注意,上文提到过:
在安全模式下,全部对状态的更新操做必须在actions中执行,不然会报错
若使用Promise、async/await来编写异步Action,在异步操做以后更新状态时,代码执行的上下文会脱离action,致使状态在action以外被更新而报错。这里有两种解决办法:
runInAction
的action在异步操做中使用// 方法1
const Model = types
.model(...)
.actions(self => ({
setLoading (loading: boolean) {
self.loading = loading;
},
setData (data: any) {
self.data = data;
},
async getData () {
...
self.setLoading(true); // 这里由于在异步操做以前,直接赋值self.loading = true也ok
const data = await api.getData();
self.setData(data);
self.setLoading(false);
...
}
}));
// 方法2
const Model = types
.model(...)
.actions(self => ({
runInAction (fn: () => any) {
fn();
},
async getData () {
...
self.runInAction(() => self.loading = true);
const data = await api.getData();
self.runInAction(() => {
self.data = data;
self.loading = false;
});
...
}
}));
复制代码
方法1须要额外封装N个action,比较麻烦。方法2封装一次就能够屡次使用。
可是在某些状况下,两种方法都不够完美:一个异步action被分割成了N个action调用,没法使用MST的插件机制实现整个异步action的原子操做、撤销/重作等高级功能。
为了解决这个问题,MST提供了flow
方法来建立异步action:
import { types, flow } from 'mobx-state-tree';
const model = types
.model(...)
.actions(self => {
const getData = flow(function * () {
self.loading = true;
try {
const data = yield api.getData();
self.data = data;
} catch (err) {
...
}
self.loading = false;
});
return {
getData
};
})
复制代码
使用flow
方法须要传入一个generator function
,在这个生成器方法中,使用yield
关键字能够resolve异步操做。而且,在方法中能够直接给状态赋值,写起来更简单天然。
snapshot即“快照”,表示某一时刻,Model
的状态序列化以后的值。这个值是标准的JS对象。
使用getSnapshot
方法获取快照:
import { getSnapshot } from 'mobx-state-tree';
cosnt Model = types.model(...);
const model = Model.create(...);
console.log(getSnapshot(model));
复制代码
使用applySnapshot
方法能够更新Model
的状态:
import { applySnapshot } from 'mobx-state-tree';
...
applySnapshot(model, {
msg: 'hello'
});
复制代码
经过applySnapshot
方法更新状态时,传入的状态值必须匹配Model
的类型定义,不然会报错:
getSnapshot
及applySnapshot
方法均可以用在Model
的子Model
上使用。
在MST中,props对应的状态都是可持久化
的,也就是能够序列化为标准的JSON数据。而且,props对应的状态必须与props的类型相匹配。
若是须要在Model中存储无需持久化,而且数据结构或类型没法预知的动态数据,能够设置为Volatile State
。
Volatile State
使用model.volatile
方法定义:
import { types } from 'mobx-state-tree';
import { autorun } from 'mobx';
const Model = types
.model('Model')
.volatile(self => ({
anyData: {} as any
}))
.actions(self => ({
runInAction (fn: () => any) {
fn();
}
}));
const model = Model.create();
autorun(() => console.log(model.anyData));
model.runInAction(() => {
model.anyData = {a: 1};
});
model.runInAction(() => {
model.anyData.a = 2;
});
复制代码
和actions及views同样,model.volatile
方法也要传入一个参数为Model
实例的方法,并返回一个对象。
运行上面代码,可获得以下输出:
代码中使用Mobx的autorun
方法监听并打印model.anyData
的值,图中一共看到2次输出:
可是第二次为anyData.a
赋值并无执行autorun。
因而可知,Volatile State
的值也是Observable
,可是只会响应引用的变化,是一个非Deep Observable
。
能够点开上面的连接,修改其中的代码,熟悉一下上面提到的几个方法的使用。
本章介绍了MST的基础概念和重要的几个API,后面会给你们讲解使用MST搭配React来实现一个完整的Todo List
demo。
喜欢本文欢迎关注和收藏,转载请注明出处,谢谢支持。