MobX

1. 引见

1.1. 道理

React的render是 状况 转化为树状构造的衬着组件的要领
而MobX供应了一种存储,更新 状况 的要领
React 和 MobX都在优化着软件开发中雷同的题目。
React 运用的要领是让假造DOM来削减烦琐而极重的DOM变化。
而MobX则经由过程一个假造的状况依靠图表来让react组件和运用状况同步化来削减不必要的状况致使组件更新

1.2. 装置

MobX:

npm install mobx --save

React bindings:

npm install mobx-react --save

1.3. 要点

MobX看起来很庞杂的模样,实际上是用它只须要三步

  1. 定义你的状况,让它们成为视察者(observable)
    存储状况(Store state)可所以任何的数据构造,随你定义为:对象,数组,类,轮回构造,援用都没所谓。但须要记着一点,就是:跟着时刻的变化,用MobX 去把它们定义成视察者(observable)

import {observable} from 'mobx'
let appState = observable({
    timer: 0
})
  1. 我们不须要让appState去视察什么。你如今就能够建立视图(view),每当appState的相干数据发生变化的时刻,就会自动更新。MobX会采纳最优的体式格局去更新你的视图。以下有一个例子来申明怎样运用,个中运用了ES6/ES7的语法(固然MobX也是支撑ES5),代码中@的意义

import {observer} from 'mobx-react';
@observer
class TimerView extends React.Component {
    render() {
        return (<button onClick={this.onReset.bind(this)}>
                Seconds passed: {this.props.appState.timer}
            </button>);
    }
    onReset () {
        //appState.resetTimer会在下一节完成
        this.props.appState.resetTimer();
    }
};
React.render(<TimerView appState={appState} />, document.body);
  1. 修正状况
    第三节要说的是修正状况。MobX和其他框架差别,它不会要求你去做什么事变,它只是协助你去做简朴的事变

appState.resetTimer = action(function reset() {
    appState.timer = 0;
});
setInterval(action(function tick() {
    appState.timer += 1;
}), 1000);

个中action包装用法只能在strict形式下运用,请记得在你的javascript文件头写上:’use strict’。

2. API

从上面的例子能够看到,MobX的API实在不多:observable, computed, reactions, actions

2.1. observable(value)

个中的value可所以JS原定的数据构造,援用,对象,数组,ES6的map

  1. 假如value是一个map的话,则须要运用一个调节器(modifier)asMap来运用。这时刻会返回一个Observable Map

  2. 假如是一个数组,返回Observable Array

  3. 假如是一个没有属性的对象,则返回一个Observable Object

  4. 假如是一个有属性的对象,JS原有的数据构造,函数等,返回一个 Boxed Observable。MobX不会自动让一个有属性的对象成为视察者。这是这个有属性的对象的构造函数应当做的事变,你能够运用extendObservable在它的构造函数内里,或许在它的类运用@observable去定义。

以下是一些例子:

const map = observable(asMap({ key: "value"}));
map.set("key", "new value");

const list = observable([1, 2, 4]);
list[2] = 3;

const person = observable({
    firstName: "Clive Staples",
    lastName: "Lewis"
});
person.firstName = "C.S.";

const temperature = observable(20);
temperature.set(25);

2.2. @observable

import {observable} from "mobx";
class OrderLine {
    @observable price:number = 0;
    @observable amount:number = 1;
    constructor(price) {
        this.price = price;
    }
    //这里在下一节会说到
    @computed get total() {
        return this.price * this.amount;
    }
}
const line = new OrderLine();
console.log("price" in line); // true
//hasOwnProperty:推断一个对象是不是有你给出称号的属性或对象。须要注重,此要领没法搜检该对象的原型链中是不是具有该属性
console.log(line.hasOwnProperty("price")); //false

假如你的环境不支撑ES6/7的语法的话,实在@observable key = value; 只是extendObservable(this, { key: value })的语法糖。因而在ES5环境下你也能运用

2.3. (@)computed

Computed values 就像一个算术公式一样去从现有的状况或其他值去盘算出须要的值。盘算的消耗是不可低估的。Computed尽量帮你削减个中的消耗。它们是高度优化的,请把它用在能够用到的处所。

不要殽杂下一节说到的autorun。虽然他们都是被动挪用的表达式。然则……
Computed运用状况:假如你须要发生一个有视察者(observers)参数盘算的新的值的时刻
autorun运用状况:你不想发生一个新的值就想到达一个新的效果/功用。就像是打log或许举行收集要求
Computed values是自动帮你从你的状况(state)值和其他盘算辅佐值来盘算的。MobX做了许多的优化。当介入盘算的值没有发生转变,Computed是不会重新运转。假如介入盘算的值没有被运用,Computed values是停息的。

假如Computed values不再是视察者(observed),那末在UI上也会把它撤除,MobX能自动做渣滓接纳。autorun则须要你本身手动去处置惩罚。假如介入盘算的值不再被运用,是不会缓存Computed的,所以重新盘算是须要的。这个是最理想的默许状况。假如你想保存,能够相识一下keepalive和observe。

例子1: 在2.2的例子。@computed get

例子2: @computed set

class Foo {
    @observable length: 2,
    @computed get squared() {
        return this.length * this.length;
    }
    set squared(value) { //this is automatically an action, no annotation necessary
        this.length = Math.sqrt(value);
    }
}

须要注重的是:setter并不是用于直接转变参数盘算的值,如例子中的length。而是作为一个逆推导。

2.4. Autorun

Autorun是用在一些你想要发生一个不必视察者介入的被动挪用函数内里。当autorun被运用的时刻,一旦依靠项发生变化,autorun供应的函数就会被实行。与之相反的是,computed供应的函数只会在他有本身的视察员(observers)的时刻才会评价是不是重新实行,不然它的值被认为是无用的。

依据这些履历:假如你须要一个自动运转但却不会发生任何新的值的效果的函数,那末请运用Autorun。其他状况请运用computed。Autorun只是作用于假如到达某个效果或许功用,而不是盘算某些值。假如有一个字符串作为第一个参数存入Autorun,那末它将成为一个调试称号。

var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

var disposer = autorun(() => console.log(sum.get()));
// prints '6'
numbers.push(4);
// prints '10'

2.5. @observer

  1. observer 函数/润饰器用于react组件。经由过程mobx-react依靠包来供应。它经由过程mobx.autorun来包装了组件的render函数,以确保组件的render函数在任何数据的变动是强迫重新衬着

import {observer} from "mobx-react";
var timerData = observable({
    secondsPassed: 0
});
setInterval(() => {
    timerData.secondsPassed++;
}, 1000);
@observer class Timer extends React.Component {
    render() {
        return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
    }
});
React.render(<Timer timerData={timerData} />, document.body);

tips: 假如另有其他的decorators一同或许高阶组件的存在,请确保observer为最内层(优先运用)的润饰器。不然它能够没法事情。假如你只在ES5的环境下事情:实在observer不过是observer(class Timer … { }) 的语法糖。

  1. 难点—组件中相干值的援用:
    MobX能做的事变许多,然则它却不能把原始的值变成视察者(只管能够经由过程包裹这个值来返回一个boxed observables的对象)。所以视察者不是这个原始的值,而是返回后的对象的属性值。修正一个适才的例子:

React.render(<Timer timerData={timerData.secondsPassed} />, document.body)

这时刻顺序并不会事情了。传入组件的只是timerData内里secondsPassed的当前值。在组件内里,它是不可变的。

  1. 把你的组件内部状况变成可视察的
    和一般的类一样,你能够在你的组件运用@observable润饰器。这意味着你的组件具有了一个内部state,而且它不须要运用react内部供应的烦琐的setState机制。这个内部state能调起render函数,然则却不能正确调起React的性命周期函数,比方:componentShouldUpdate / componentWillUpdate。假如你想要这些,最好运用react供应的API来建立state。固然也能够如许写

import {observer} from "mobx-react"
import {observable} from "mobx"
@observer class Timer extends React.Component {
    @observable secondsPassed = 0
    componentWillMount() {
        setInterval(() => {
            this.secondsPassed++
        }, 1000)
    }
    render() {
        return (<span>Seconds passed: { this.secondsPassed } </span> )
    }
})
React.render(<Timer />, document.body)
  1. 衔接observer和stores
    mobx-react供应了Provider组件让你能够把通报下来的stores作用在react供应的上下文机制。经由过程衔接这些stores和observer,这些observer会成为组件的属性来运用。

const colors = observable({
   foreground: '#000',
   background: '#fff'
});
const App = () =>
  <Provider colors={colors}>
     <app stuff... />
  </Provider>;
const Button = observer(["colors"], ({ colors, label, onClick }) =>
  <button style={{
      color: colors.foreground,
      backgroundColor: colors.background
    }}
    onClick={onClick}
  >{label}<button>
);
// later..
colors.foreground = 'blue';
// all buttons updated
  1. componentWillReact
    React 的组件老是重新的客栈去衬着。因而让它它很难推断一个组件是不是须要重新衬着。在mobx-react内里,你能够运用重新定义的性命周期componentWillReact。它只会在视察者发生变化的时刻才重新衬着。

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>;
    }
}

componentWillReact没有任何参数,而且不会在render初始化之前实行(componentWillMount的区分)。而当吸收新的属性或许setState以后,它会被挪用。

2.6. action

  1. 任何运用顺序都有操纵(action)。action是任何转变状况的事物。运用MobX,您能够经由过程标记它们在您的代码中显式地显现您的操纵(action)。它会更好的协助你构造你的代码。发起将它们用于修正可视察量或具有副作用的任何函数中。
    须要注重的是:action是用在strict mode 中的

action(fn)
action(name, fn)
@action classMethod() {}
@action(name) classMethod () {}
@action boundClassMethod = (args) => { body }
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}
@action.bound(function() {})
@action createRandomContact() {
    this.pendingRequestCount++;
    superagent
         .get('https://randomuser.me/api/')
         .set('Accept', 'application/json')
         .end(action("createRandomContact-callback", (error, results) => {
                 if (error) console.error(error)
                 else {
                     const data = JSON.parse(results.text).results[0];
                     const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture)
                     contact.addTag('random-user');
                     this.contacts.push(contact);
                     this.pendingRequestCount--;
                 }
        }
))}
  1. action 仅仅作用于当前运转的函数,而不能作用于当前函数挪用的函数。这意味着在一些定时器或许收集要求,异步处置惩罚的状况下,它们的回调函数没法对状况做成转变。这些回调函数都应当有action包裹,假如例子内里的 createRandomContact-callback 一样。然则,假如你运用了async / await的话,最好的体式格局应当是运用 runInAction 来让它变得越发简朴

@action /*optional*/ updateDocument = async () => {
    const data = await fetchDataFromUrl();
    /* required in strict mode to be allowed to update state: */
    runInAction("update state after fetching data", () => {
        this.data.replace(data);
        this.isSaving = true;
    })
}
  1. Bound actions
    现在看到的actions都是遵照在javascript中绑定的一般划定规矩,然则在MobX 3引入了action.bound来自动绑定actions到目的对象上。和action的运用不一样,不须要一个名字参数。它的称号一直基于绑定到属性的操纵上。须要注重的是,在箭头函数上不要如许运用,由于箭头函数已绑定了上下文,不能在重新变动上下文

class Ticker {
    @observable this.tick = 0

    @action.bound
    increment() {
        this.tick++ // 'this' will always be correct
    }
}
const ticker = new Ticker()
setInterval(ticker.increment, 1000)

跋文

启动例子项目:进入

  1. 前端项目:npm install 或许 yarn install 翻开当地的8080端口

  2. 后端部份:进入 back文件夹,实行npm start 翻开当地的3000端口

  3. 作为对照:运用redux的例子:进入

    原文作者:三月懒驴
    原文地址: https://segmentfault.com/a/1190000008267003
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞