开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看活动详情
mobx
概念
MobX是响应式编程,实现状态的存储和管理。使用MobX将应用变成响应式可归纳为三部 曲:
- 定义状态并使其可观察
- 创建视图以响应状态的变化
- 更改状态
响应式
mobx和vue一样的数据监听,底层通过Object.defineProperty或Proxy来劫持数据,可以做到更细粒度的渲染。
在react中反而把更新组件的操作(setState)交给了使用者,由于setState的"异步"特性导致了没法立刻拿到更新后的state。
依赖收集
在mobx中,通过autorun和reaction对依赖的数据进行了收集(可以通过get来收集),一旦这些数据发生了变化,就会执行接受到的函数,和发布订阅很相似。
mobx-react中则提供了observer方法,用来收集组件依赖的数据,一旦这些数据变化,就会触发组件的重新渲染。
管理局部状态
在react中,我们更新状态需要使用setState,但是setState后并不能立马拿到更新后的state,虽然setState提供了一个回调函数,我们也可以用Promise来包一层,但终究还是个异步的方式。
在mobx中,我们可以直接在react的class里面用observable声明属性来代替state,这样可以立马拿到更新后的值,而且observer会做一些优化,避免了频繁render。
@observer
class App extends React.Component {
@observable count = 0;
constructor(props) {
super(props);
}
@action
componentDidMount() {
this.count = 1;
this.count = 2;
this.count = 3;
}
render() {
return <h1>{this.count}</h1>
}
}
react-mobx
computed
计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。简单理解为对可观察数据做出的反应,多个可观察属性进行处理,然后返回一个可观察属性使用方式:1、作为普通函数,2、作为decorator
import {observable} form 'mobx'
class Store{
@observable arr = [];
@observable obj = {};
@observable mao = new Map();
@observable num = 1;
@observable str = 'str';
@observable bool = true;
// 2. 作为decorator
@computed get mixed(){
return store.str + '/'+ store.num
}
}
const store = new Store();
// 1. 作为普通函数
let foo = computed(function(){
return store.str + '/'+ store.num
})
// computed 接收一个方法,里面可以使用被观察的属性
// 监控数据变化的回调,当foo里面的被观察属性变化的时候 都会调用这个方法
foo.observe(function(change){
console.log(change) // 包含改变值foo改变前后的值
})
store.str = '1';
sotre.num = 2;
autorun
当我们使用decorator来使用computed,我们就无法得到改变前后的值了,这样我们就要使用autorun方法。从方法名可以看出是“自动运行”。所以我们要明确两点:自动运行什么,怎么触发自动运行自动运行传入autorun的参数,修改传入的autorun的参数修改的时候会触发自动运行
import {observable,autorun} form 'mobx'
class Store{
@observable arr = [];
@observable obj = {};
@observable mao = new Map();
@observable num = 1;
@observable str = 'str';
@observable bool = true;
// 2. 作为decorator
@computed get mixed(){
return store.str + '/'+ store.num
}
}
const store = new Store();
autorun(() => {
console.log(store.str + '/'+ store.num)
})
store.str = '1';
sotre.num = 2;
when
用法: when(predicate: () => boolean, effect?: () => void, options?)
when 观察并运行给定的 predicate,直到返回true。 一旦返回 true,给定的
effect 就会被执行,然后 autorunner(自动运行程序) 会被清理。
该函数返回一个清理器以提前取消自动运行程序。
when方法接收两个参数(两个方法),第一个参数根据可观察属性的值做出判断
返回一个boolean值,当为true的时候,执行第二个参数。如果一开始就返回
一个true,就会立即执行后面的方法。
import {observable,when} form 'mobx'
class Store{
@observable arr = [];
@observable obj = {};
@observable mao = new Map();
@observable num = 1;
@observable str = 'str';
@observable bool = false;
// 2. 作为decorator
@computed get mixed(){
return store.str + '/'+ store.num
}
}
const store = new Store();
when(() => store.bool,()=> {
console.log('it's a true)
})
store.bool = true;
computed
computed是基于现有状态或计算值衍生出的值
如下面todoList的例子,一旦已完成事项数量改变,那么completedCount会自动更新。
class TodoStore {
@observable todos = []
@computed get completedCount() {
return (this.todos.filter(todo => todo.isCompleted) || []).length
}
}
reaction
reaction则是和autorun功能类似,但是autorun会立即执行一次,而reaction不会,使用reaction可以在监听到指定数据变化的时候执行一些操作,有利于和副作用代码解耦。
// 当todos改变的时候将其存入缓存
reaction(
() => toJS(this.todos),
(todos) => localStorage.setItem('mobx-react-todomvc-todos', JSON.stringify({ todos }))
)
用法:reaction(() => data, (data, reaction) => { sideEffect }, options?)
它接收两个函数参数,第一个(数据函数)是用来追踪并返回数据作为第二个函数(效果函数)的输入。不同于 autorun的是当创建时效果函数不会直接运行(第二个参数不会立即执行, autorun会立即执行传入的参数方法),只有在数据表达式首次返回一个新值后才会运行。在执行效果函数时访问的任何 observable 都不会被追踪。
import {observable,reaction} form 'mobx'
class Store{
@observable arr = [];
@observable obj = {};
@observable mao = new Map();
@observable num = 1;
@observable str = 'str';
@observable bool = false;
// 2. 作为decorator
@computed get mixed(){
return store.str + '/'+ store.num
}
}
const store = new Store();
reaction(() => [store.str,store.num],(arr) => console.log(arr.join('\')))
store.str = '1';
sotre.num = 2;
action:
在redux中,唯一可以更改state的途径便是dispatch一个action。这种
约束性带来的一个好处是可维护性。整个state只要改变必定是通过action触发的,对此只要找到reducer中对应的action便能找到影响数据改变的原因。强约束性是好
的,但是Redux要达到约束性的目的,似乎要写许多样板代码,虽说有许多库都在解决该问题,然而Mobx从根本上来说会更加优雅。 首先Mobx并不强制所有state的改变必须通过action来改变,这主要适用于一些较小的项目。对于较大型的,需要多人合作的项目来说,可以使用Mobx提供的api configure来强制。
observer
mobx-react的observer就将组件的render方法包装为autorun,所以当可观察属性的改变的时候,会执行render方法。
observable
observable是一种让数据的变化可以被观察的方法
observable(value) 是一个便捷的 API ,此 API 只有在它可以被制作成可观察的数据结构(数组、映射或observable 对象)时才会成功。对于所有其他值,不会执行转换。
项目工程化
mobx中的store的创建偏向于面向对象的形式
action和dataModel一起组合成了页面的总store,dataModel只存放UI数据以及只涉及自身数据变化的action操作(在mobx严格模式中,修改数据一定要用action或flow)。
action store则是负责存放一些需要使用来自不同store数据的action操作。
dataModel更像MVC中的model,action store是controller,react components则是view,三者构成了mvc的结构。
- stores
- actions
- hotelListAction.js
- dataModel
- globalStatus.js
- hotelList.js
- index.js
// globalStatus
class GlobalStatus {
@observable isShowLoading = false;
@action showLoading = () => {
this.isShowLoading = true
}
@action hideLoading = () => {
this.isShowLoading = false
}
}
// hotelList
class HotelList {
@observable hotels = []
@action addHotels = (hotels) => {
this.hotels = [...toJS(this.hotels), ...hotels];
}
}
// hotelListAction
class HotelListAction {
fetchHotelList = flow(function *() {
const {
globalStatus,
hotelList
} = this.rootStore
globalStatus.showLoading();
try {
const res = yield fetch('/hoteList', params);
hotelList.addHotels(res.hotels);
} catch (err) {
} finally {
globalStatus.hideLoading();
}
}).bind(this)
}