Mobx是如何工做的

mobx工做原理

推薦版本: "mobx-react": "4.1.0", "mobx": "^2.7.0 || ^3.0.0"react

1 Mobx 要點

1.1 定義狀態並使其可觀察

能夠任何數據結構來存儲狀態,如對象、數組、類,打上mobx的標記會變爲可觀察。git

import { observable } from 'mobx';
var appStore = observable({
    timer: 0;
});

1.2 建立視圖響應狀態變化

mobx 以最小限度更新視圖,任何函數均可以成爲響應式視圖觀察自身數據。 github

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() {
        this.props.appState.resetTimer();
    }
};
ReactDOM.render(<TimerView appState={appStore} />, document.body);

1.3 更改狀態

mobx會用簡單直觀的方式更改狀態,使用action(能夠配置Mobx強制使用action更新)或者直接修改web

2 概念及原則

2.1 State 狀態

狀態是驅動應用的數據。像待辦事務列表的特定狀態,還有像當前已選元素的視圖狀態。 
狀態就像有數據的 Excel表格。算法

2.2 derivations 衍生

什麼是衍生, 源自狀態而且不會再有進一步的相互做用的東西就是衍生。 express

  • 用戶界面 
  • 衍生數據,剩下的待辦事項 
  • 後端集成,好比吧變化發送到服務器端 
    mobx 集成了兩種類型的衍生

Computed values計算屬性值

使用純函數從當前可觀察狀態中衍生出的值。 編程

Reactions 反應

是當 State 改變時須要自動發生的反作用。。須要有一個橋樑連接 函數式編程 和 響應式編程 
初次使用mobx會常用reactions,可是推薦使用computed,回到表格的概念,公式是計算值的衍生。 redux

Actions 動做

動做 任意一段能夠改變狀態的代碼。 用戶操做,後端數據推送,預約事件等。 
動做 相似在表格單元格中輸入一個新值。 mobx中能夠顯示的定義動做,@action . 後端

3 原則

mobx支持單向的數據流,動做來改變狀態,從而 狀態State 的改變會更新受影響的視圖。 
action ---> state ---> view 
當 State 改變時,全部衍生都會進行原子級自動更新 所以不可能觀察到中間值 
所有的 衍生 默認都是同步更新 所以在動做以後,能夠安全的檢查計算值 
計算值 是延遲更新的。 任何不在使用狀態的計算值都不會更新,直到須要時會進行 反作用(IO),不使用時會自動垃圾回收 
計算值 不該該去改變狀態,應該是一個純潔的反作用。 api

4 核心API

主要api: Computed 、 observable 、 reactions 、 actions

4.1 observable

observable(value);
@observable property = value;

Observable 觀察的值能夠是基本類型、引用類型、普通對象、類實例、數組和映射。 
主要類型轉換規則,或者經過裝飾器微調(修飾class,函數)

  • 若是被觀察 value 是ES6實例,會返回一個新的Observe Map,基於ES6。若是不僅是在更改某個entry時修改 
    而是,在添加或刪除其餘entry時作出反應,Observe Map 會頗有用。 
  • 若是被觀察 value 是數組,會返回一個 Observe Array 
  • 若是 value 是麼有原型的對象(對象能夠滅有原型)或者原型是 Object.prototype ,對象會被克隆而且全部屬性會被轉換成可觀察的 Observe Object 
  • 若是 value 是有原型的,例如函數,數組,能夠有4中方法處理 Boxed Observer
    • 顯示的調用 observable.box(value) 有點神奇
    • @observable 經常使用
    • 調用 decorate()
    • 類中引入 extendObservable() 來引入屬性 可用 裝飾器默認是有感染的,observalbe 被自動應用於數據結構包含的任何值,
      observable 是 extendObservable(this, {prototype: value}) 的語法糖 observable.object(obj, decorator, option) 默認這些值都會轉換成可觀察 
      observable.array(obj, option) 會生成一個observable 數組,若是不想每一個值都被觀測,可設置 {deep: false} 
      observable.map(obj, option) 無需侷限於字符串

4.2 裝飾器 Decorator

可用裝飾的列表是這些:

  • observable.deep 默認的 observable 裝飾器 
  • computed 建立一個衍生,就是能自動獲取已修改值的函數並返回新值 
  • action 建立 動做 
  • action.bound 建立有範圍的動做 
class TodoList {
    todos = {}
    get unfinishedTodoCount() {
        return values(this.todos).filter(todo => !todo.finished).length
    }
    addTodo() {
        const t = new Todo()
        t.title = 'Test_' + Math.random()
        set(this.todos, t.id, t)
    }
}
decorate(TodoList, {
    todos: observable,
    unfinishedTodoCount: computed,
    addTodo: action.bound
})   // 對類 Observable 轉換

4.3 計算屬性 Computed

用法好幾種,看起來只有一些細微的差異:

  • computed( () => expression) 
  • computed( () => expression, (newvalue) => void ) 
  • computed( () => expression, option ) 
    @computed({ equals: compareFn }) get property() { return expression; } 
    @computed get classProperty() { return expression; } 

Computed 自帶不少操做屬性 控制 Computed 行爲 

  • 比較器算法 equals: (value, value) => boolean 用來重載默認檢測規則的比較函數。 內置比較器有: comparer.identity, comparer.default, comparer.structural 
  • 追蹤 其餘observable 類型屬性值,等待返回以後在作計算 requiresReaction: boolean 在從新計算衍生屬性以前,等待追蹤的 observables 值發生變化

- get: () => value 
- set: (value) => void 
- keepAlive: boolean 保持計算值活動,不光是在值發生變化以後。 

4.4 動做 Actions

任何用來 修改狀態 的東西 
建議在任何更改 observable 或者有反作用的函數上進行 Actions修飾 

4.5 流處理 Flow

flow(function* (args) {}) 
flow() 接收 generator 函數做爲他的惟一輸入 
flow 的關鍵做用是 處理異步代碼時確保代碼被action包裝 ,由於正常的 observable state 對異步操做沒法經過 enforceActions 檢查。 
神奇的flow能夠解決這個異步不跟蹤的問題 
注意,異步函數必須是 generator ,並且在內部只能 yield promises 

import { configure, flow } from 'mobx';

// 不容許在動做外部修改狀態 嚴格模式的意思
configure({ enforceActions: true });

class Store {
    @observable githubProjects = [];
    @observable state = "pending"; // "pending" / "done" / "error"


    fetchProjects = flow(function* fetchProjects() { // <- 注意*號,這是生成器函數!
        this.githubProjects = [];
        this.state = "pending";
        try {
            const projects = yield someAsyncProcess(); // 用 yield 代替 await
            const filteredProjects = somePreprocessing(projects);

            // 異步代碼自動會被 `action` 包裝
            this.state = "done";
            this.githubProjects = filteredProjects;
        } catch (error) {
            this.state = "error";
        }
    })
}

Flows 能夠撤銷,調用promise的cancel() 方法會中止異步狀態取值, 會繼續執行 finally 子句 。

5 observables 作出響應

5.1 computed

計算值是能夠根據現有的狀態或其餘計算值衍生的值。 
概念上來說,他們和表格中的值十分類似,好比彙總80分以上的同窗。 
計算屬性 可使實際可修改的值儘量的小,計算屬性也是高度優化過的,能夠多用 

5.2 computed & autorun

聲明式的建立計算屬性,能夠在類任意的屬性上使用裝飾器

import { observable, computed } from 'mobx';

class orderline {
    @observable price = 10;
    @observable amount = 1;

    constructor(price) {
        this.price  = price;
    }
    @computed get total() {
        return this.price * this.amount;
    }

    
}
import { observable, autorun } from 'mobx'; const value = observable(0); const number = observable(100); autorun(() => { console.log(value.get()); }); value.set(1); value.set(2); number.set(101); // 0 1 2 不打印 101 yinwei number 未在autorun內部執行 number.get()/
Mobx 學習 基本寫法 * 此處聲明式的監控變量,與 ES6 的類修飾不一樣。

import { observable, action, computed, toJS } from 'mobx' import { observer } from 'mobx-react'

export default class InstanceStore { @observable value = 1

@action
modifyValue(v) {
    this.value = v
}
@computed get getValue() {
    return this.value * 10;
}

}

computed 直接獲取一個計算後的值。

若是一個值須要根據某個state計算,而且也須要被觀察則可使用 @computed autorun 相似

autorun 用於執行一些和值變化有關的操做,好比異步請求,數據處理等

computed 用於根據已有的值,計算出新的值返回一個對觀察值追蹤的結果 var ins = new InstanceStore(); console.log('value form mobx computed', toJS(ins.getValue()))

autorun 在不須要繼續使用的狀況能夠進行垃圾回收

var numbers = observable([1,2,3]);

var sum = computed(() => numbers.reduce((a, b) => a + b, 0));

var disposer = autorun(() => console.log(sum.get())); // '6'

numbers.push(4);                                                  // '10'

disposer();

numbers.push(5); // 什麼也不打印,由於disposer執行是再也不對autorun reaction

過時狀態值方式以下

var ins = new InstanceStore();

console.log(toJS(ins.value),'get value from mobx');

dispatch 修改值 var ins = new InstanceStore(); ins.modifyValue(1000);

在組件內可使用觀察者模式 @observer class routeCreate extends Component { constructor(props) { super(props); this.store = new InstanceStore(); } ... }

使用 observer 修飾組件,而且在render內部有 mobx 值的引用,組件會多一個生命週期 componentWillReact // redux 改變值的方式是經過拷貝原來的對象生成新的對象,觸發組件的componentWillReceiveProps // mobx 是以原始值的基礎上生成新的對象,以前的引用不變因此mobx 不會觸發ReceiveProps週期

 異步處理
mobx 狀態值爲同步更新。

export default class InstanceStore { @observable value = 1

@action
modifyValue(v) {
    this.value = v;
    setTimeout(this.valueAdd, 100);
}
@action.bound
valueAdd(v) {
    this.value = v + 20;
}

}

// .bound 是js執行環境語法糖
// 過多action ? 須要簡化寫法
// mobx 自身提供了一個工具函數幫助跟新對應action中的值 runInAction

export default class InstanceStore {

   @observable value = 1

@action
asyncModifyValue(v) {
    this.value = v;
    setTimeout(action('valueAdd', () => {
        this.value = v + 20
    }), 100);
}

@action
asyncModify(v) {
    this.value = v;
    setTimeout(runInAction(() => {
        this.value = v + 20
    }), 100);
}

}

//  異步action,action能夠這樣寫
@asyncAction
changeValue() {
    this.value = 0;
    const data = yield Promise.resolve(1)
    this.value = data;
}
toJS 將mobx state 序列轉換爲js可識別類型?

更新action的約束
mobx 非強制使用action改變state;若是要增強制action觸發state能夠經過 
Mobx.configure({enforceActions: true}) 加限制條件,強制經過action更新,適用於大型項目



如下是遺留問題
* 1, mobx 是不是同步更新 是
* 2, mobx toJS是如何實現的  
* 3,store對應單個變量,會按照類型預留數組空間,是什麼緣由
* 4,使用toJS獲取數據,須要在class名稱上面加 @observer 嗎
* 5,爲何mobx取值,是如此的簡介?,並且是支持多狀態
* 6,extendObservable 能夠按照擴展的方式 裝飾函數或class裏的對象
相關文章
相關標籤/搜索