Mobx是如何工作的


mobx工作原理

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

1 Mobx 要點

1.1 定義狀態並使其可觀察

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

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

1.2 創建視圖響應狀態變化

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

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更新)或者直接修改

2 概念及原則

2.1 State 狀態

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

2.2 derivations 衍生

什么是衍生, 源自狀態並且不會再有進一步的相互作用的東西就是衍生。 

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

Computed values計算屬性值

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

Reactions 反應

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

Actions 動作

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

3 原則

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

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里的對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM