理解和学习mobx-react

3,633 阅读4分钟
原文链接: github.com

理念

MobX 是一个非常优雅的状态管理库,它的理念是通过观察者模式对数据做出追踪处理,在对可观察属性的作出变更或者引用的时候,触发其依赖的监听函数。这一点和Vue通过Object.defineProperty ,在对状态进行读写操作的时候会触发其 getter 和 setter 函数以进行响应的原理其实是非常类似的。

对于 React,你只需增加 observer 装饰器,即可将你的无状态组件转换为可响应组件:

const { observable, action, autorun } = require('mobx');

class Store {
    count = 0;
    @action add () {
        this.count = ++this.count;
    }
}
const mstore = new Store();
setInterval(() => {
    mstore.add();
}, 800);
autorun(() => {
    console.log(mstore.count);
});

在该例子中使用 autorun 的时候调用了一次其传入的函数,之后mstore.count 的值即使改变也并没有触发观察,这是因为 mstore.count 并不是可观察的。这时,只需加一个@observable count = 0;将其设为可观察,即可对相应的值的变化做出相应的动作.
Mobx数据流是单向的:
image

在整个数据流中,通过事件驱动(UI 事件、网络请求…)触发 Actions,在 Actions 中修改了 State 中的值,这里的 State 即应用中的 store 树(存储数据),然后根据新的 State 中的数据计算出所需要的计算属性(computed values)值,最后响应(react)到 UI 视图层。

@observable

observable 的属性值在其变化的时候 ,mobx 会自动追踪并作出响应。其语法为:

import {observable} from "mobx";
import { observer } from "mobx-react";
@observable  classProperty = value

其核心原理是 Object.defineProperty ,给被包装的属性套上 getter 和 setter 的钩子,在 get 中响应依赖收集,在 set 中触发监听函数。

@Observer

@observermobx-react提供的,通过使用@observer,将react组件转换成一个监听者,这样在被监听的应用状态变量(Observable)有更新时,react组件就会重新渲染。当 render 中的 state发生改变时, mobx-react 会重新调用 render 方法,重新渲染这个组件。如下
,TodoItemModel中的应用状态变量有更新时,TodoItem UI会重新渲染:

class TodoItemModel {
    id
    @observable title;
    @observable completed;
    ......
}

@observer
class TodoItem extends React.Component {
    .....
    render() {
        //UI logic code ...
        let todo = this.props.todo;
        let title = todo.title;
        let complete = todo.completed;
        return (
            ....
        );
    }
}

@computed

其语法为:

@computed get computesValue [function];

例子看下面 :point_down:

@action

@action,其规定对于 store 对象中所有可观察状态属性的改变都应该在 @action 中完成,凡是涉及到对应用状态变量修改的函数,都应该使用@action修饰符。

语法形式:@action actionFuncName[function]

例子:

const { observable, action, computed, autorun } = require('mobx');

class Store {
    @observable list = []
    @computed get total() {
        return this.list.length;
    }
   @action change (i) {
        this.list.push(i++);
    }
}
const mstore = new Store();
autorun(() => {
    console.log(mstore.total);
});
mstore.change(1)

可以看到,store 中 list 属性的改变都放在 @action change 函数之中,外加只需要调用该函数即可。

运行观察autorun

在上面的例子中,当触发了可观察状态属性的改变后,其变化的监听则是在传入 autorun 函数中作出响应。

autorun传入一个函数,当该函数中依赖的可观察状态属性(或者计算属性)发生变化的时候,该函数会被调用。这个函数只会观察自己依赖到的设为 observable 的值。

class Store {
    @observable count = 0;
    @action add () {
        this.count = this.count + 1;
    }
}
const mstore = new Store();
var sId = setInterval(() => {
    mstore.add();
}, 2000);

autorun(() => {
    if(mstore.count >2){
        clearInterval(sId)
    }
    console.log(mstore.count);
});

autorun 的函数依赖了mstore.count 属性,该属性是可观察的,其每次变化都会加 1 ,因此其中的函数在第一次立即触发,之后每次改变 mstore.count 的值都会被触发;当该为大于2时,清空了定时器,该值不再变化,autorun函数也不再触发

Provider组件

在react中,mobx-react提供了 Provider 组件用来包裹最外层组件节点,并且传入 store(通过)context 传递给后代组件:

import { Provider } from 'mobx-react';
const stores = {
  ...
};
ReactDOM.render((
  <Provider {...stores}>
    <App />
  </Provider>
), document.getElementById('root'));

@Inject

@inject 是为了向当前被装饰的组件 注入 store 这个props。当然 store 这个 prop 其实是由 Provider 提供的

配置

在react中,通过.babelrc即可配置es7的装饰器语法:

{
  "presets": [
    "es2015",
    "stage-1",
    "react"
  ],
  "plugins": ["transform-decorators-legacy"]
}

当然,你需要

$ npm i babel-preset-{es2015,stage-1,react} babel-plugin-transform-decorators-legacy

对于NodeV5以上版本,无需--save就会自动帮你安装到dependencies中