cp from : https://blog.csdn.net/smk108/article/details/84960159
通過《Mobx教程(一)-Mobx簡介》我們簡單理解了Mobx的基本原理及流程,使用Mobx實現一個響應式的應用,主要分為三個步驟:
定義狀態並使其可觀察(state)
對狀態改變的響應(computed、autorun、reaction、observer、when)
改變狀態(action)
下面從這三個步驟的順序介紹Mobx的主要概念。(版本為5.X、使用es7裝飾器)
1、可觀察狀態
狀態是驅動應用的數據,是應用的核心。狀態可以是與應用相關的各種數據,通常有像待辦事項列表這樣的領域特定狀態,還有像當前已選元素的視圖狀態,當然,你也可以向Redux類似,將state分為三類:與領域直接相關的領域狀態數據,反應用戶行為的應用狀態數據、視圖相關的UI狀態數據。使用Mobx一般還會創建一個或多個store用來管理state。
Mobx提供了observable和@observable兩個API用來定義可觀察的state。
observable(value) @observable classProperty = value
可以使用這兩個API定義可觀察state的數據類型可以是JS基本數據類型、引用類型、普通對象、類實例、數組和映射。observable對不同類型有不同的轉換規則,詳細解釋如下:
注:下文提到的會觸發更新都是指受其影響的computed value(被依賴)和reaction都會自動更新。
(1)JS基本數據類型
JavaScript 中的所有原始類型值都是不可變的,因此值並不會是可觀察的,Mobx會將包含值的屬性轉換成可觀察的。可以如下定義:
@observable count = 1; @observable city = 'shanghai';
當再改變count和city的值時,會觸發更新。
(2)Array
如果 value 是數組,會返回一個 Observable Array。
@observable list = ['a', 'b', 'c', 'd'];
對於對象數組,observable也會遞歸地作用於數組的每個元素對象,其元素對象的屬性也會是可觀察的。
@observable todos = [ {'id': 1, 'taskName': 'task1', 'finished': true}, {'id': 2, 'taskName': 'task2', 'finished': false}, {'id': 3, 'taskName': 'task3', 'finished': true}, {'id': 4, 'taskName': 'task4', 'finished': false}, {'id': 5, 'taskName': 'task5', 'finished': true}, {'id': 6, 'taskName': 'task6', 'finished': false}, ];
當數組元素的增加、減少或者元素對象的屬性改變時,會觸發更新。
(3)普通對象
對於沒有原型或原型是Object.prototype的普通對象,那么對象會被克隆並且所有的屬性都會被轉換成可觀察的。
{'id': 1, 'taskName': 'task1', 'finished': true}
Id、taskName、finished都會是可觀察的,這些屬性值的改變都會觸發更新。
如果屬性的值是一個普通對象,會繼續被observable處理,將其屬性轉換為可觀察的,一直如此遞歸執行。
'id': 1, 'taskName': 'task1', 'finished': {'part1': true, 'part2':false}}
part1和part2也會是可觀察的,其值的修改依然會觸發更新。
如果以后給可觀察屬性再賦值是一個普通對象時,新的普通對象值的屬性也將被轉換為可觀察的。
若一個可觀察對象的值為:
{'id': 1, 'taskName': 'task1', 'finished': true}
之后被修改為
{'id': 1, 'taskName': 'task1', 'finished': {'part1': true, 'part2':false}}
part1和part2依然是可觀察的,其值的修改依然會觸發更新。
對於5.x版本,以后新增加的屬性也會是可觀察的,4.x及以下版本是不可觀察的。
若一個可對象的值為:
{'id': 1, 'taskName': 'task1', 'finished': true}
后面被追加一個屬性
{'id': 1, 'taskName': 'task1', 'finished': true, owner: admin}
后添加的屬性owner在5.x版本依然是可觀察的。
我測試中發現刪除對象的屬性是不會觸發更新的。
(4)Map
對於ES6的Map : 會返回一個新的 Observable Map。Map對象的每一個元素都是可觀察的,並且向Map對象中添加和刪除新的元素也是可觀察的。
@observable task = new Map([['taskName', 'tank1'],['finished', true]]);
再執行
this.task.set('owner', 'admin');
owner也是可觀察的,修改會觸發更新。
(5)非普通對象
非普通對象是指構造函數為自定義函數的對象,非普通對象的屬性不會受observable的影響而轉換為可觀察的,如果需要將非普通對象的屬性轉換為可觀察的,需要在其自定義的構造函數中進行處理。例如:
class User{ @observable name; @observable role; constructor(name, role){ this.name = name; this.role = role; } } user = new User('admin', '管理員');
之后在對user實例的name或role進行修改,是會觸發更新的。
注:對js基本數據類型和非普通對象,observable返回的其實是一個特殊的boxed values類型的可觀察對象,保存的是一個指向原基本數據類型或對象的引用,這個引用是可觀察的。
2、對狀態改變的響應
本段介紹的是Mobx中的衍生(derivation),derivation是指可以從state中衍生出來的任何東西,可以是值(computed value)或者動作(autorun、reaction等)。Derivation可以自動響應state的變化而進行更新或執行有副作用的函數。
(1)Computed
計算值(computed values)是可以根據現有的狀態或其它計算值衍生出的值,應該由純函數產生,計算值可以被其它 observer 使用,例如被ui使用。
Mobx提供了computed和@computed用於定義計算值:
attr = computed(() => value); @computed get attr(){ return value; }
用法如下:
@observable list = ['a', 'b', 'c', 'd']; @computed get listLength(){ return this.list.length; }
計算值依賴的observable沒有改變,計算值不會更新
計算值采用延遲更新策略,當有其它計算值或reaction在使用時,才會計算
計算值不在被使用時,默認會被回收,也不再更新
計算值可以幫助實現實際可修改的狀態盡可能的小,又有上述優化,可以盡量多的使用
(2)autorun
當你想創建一個永遠不會被觀察的響應式函數時,可以使用autorun。
disposer = autorun(() => {sideEffect});
當autorun依賴的狀態變化時會自動執行參數的function,返回值disposer是autorun的清除函數,當不再需要autorun時,可以調用disposer清除autorun。
autorun參數中的函數在使用autorun時會立即執行一次,然后在每次它依賴的state發生變化時自動執行一次。
用法舉例:
disposer = autorun(() => { console.log(`autorun : Now the active ID is ${this.activeItem }`); });
autorun不會產生新值,是基於執行函數產生效果
autorun不會被觀察,是反應式代碼橋接到命令式代碼的方式
可用於打印日志、更新UI的代碼、發起請求
autorun第一個參數是函數,可接受第二個參數,處理delay、name、error等
(3)reaction
reaction是autorun的變種,第一個參數為數據函數,第二個參數為效果函數,數據函數返回一個值,作為效果函數的參數,效果函數不會對依賴的狀態作出反應,只有數據函數返回的值變化時才會執行。
reaction = reaction(() => data, (data, reaction) => { sideEffect })
用法舉例:
@observable todos = [ {'id': 1, 'taskName': 'task1', 'finished': true}, {'id': 2, 'taskName': 'task2', 'finished': false}, {'id': 3, 'taskName': 'task3', 'finished': true}, {'id': 4, 'taskName': 'task4', 'finished': false}, {'id': 5, 'taskName': 'task5', 'finished': true}, {'id': 6, 'taskName': 'task6', 'finished': false}, ]; reaction = reaction(() => this.todos.filter(item => item.finished === false), notFinished => { if(notFinished.length === 0){ console.log('All tasks have been completed, and autorun and reaction are no longer executed.'); this.disposer(); this.reaction(); }else { console.log(`reaction : Now the active ID is ${this.activeItem}`); } });
在上面的例子中,reaction的第一個參數會為第二個參數提供notFinished,然后第二個參數是效果函數,會執行打印日志的操作,當todo都已經完成時,會調用disposer和reaction清除auto和reaction。
reaction 返回一個清理函數,不需要再執行時應該調用清理函數
數據函數對state的改變作出反應
效果函數僅對數據函數中訪問的數據作出反應,不對state的改變作出反應
autorun第一個參數是函數,可接受第二個參數,處理delay、name、error等
(4)observer(其實也是autorun)
observer 函數/裝飾器可以用來將 React 組件轉變成響應式組件,observer 是由單獨的 mobx-react 包提供。
mobx.autorun 包裝了組件的 render 函數以確保任何組件渲染中使用的數據變化時都可以強制刷新組件。
@observer class MyComponent extends React.Component{...}
用法舉例:
import React, {Component} from 'react'; import {inject, observer} from 'mobx-react'; import {Button} from 'antd'; import Timer from '../Timer'; import './style.css'; @inject( 'userStore') @observer export default class User extends Component{ constructor(props){ super(props); this.state = {}; } render(){ const {user} = this.props.userStore; return( <div className='user'> <div className='user_list'>name:{user.name}</div> <div className='user_list'>role:{user.name}</div> <div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div> <Button type='primary' onClick={() => this.props.userStore.changeUser()}>Change User</Button> <Timer /> </div> ); } }
observer 需要組合其它裝飾器或高階組件時, observer需要是是最深處(第一個應用)的裝飾器,否則可能不能正常更新。
響應式組件沒有或很少有狀態,因為在與其他組件共享的對象中封裝(視圖)狀態通常更方便。但你仍然可以自由地使用狀態。
@observer 以和 PureComponent 同樣的方式實現了 shouldComponentUpdate,因此子組件可以避免不必要的重新渲染。
響應式組件單方面加載數據,即使子組件要重新渲染,父組件也不會進行不必要地重新渲染。
(5)when
when(() => condition, () => {sideEffect})
when會自動響應它使用state的變化,也就是會觀察並運行第一個參數,直到condition返回true。 一旦返回 true,給定的sideEffect就會被執行,且只會執行一次,然后 autorunner(自動運行程序) 會被清理。 該函數返回一個清理器以提前取消自動運行程序。
用法舉例:
constructor(){ when(() => this.hasNotFinished.length === 0, () => { this.disposer(); this.reaction(); }); } @computed get hasNotFinished(){ return this.todos.filter(item => item.finished === false); }
when非常適合用在以響應式的方式執行取消或清除邏輯的場景
when最多執行一次
when返回一個清理器,可以在需要時提前清除
(6)mobx對什么作出反應
MobX 會對在追蹤函數執行過程中讀取現存的可觀察屬性做出反應。
“讀取” 是對象屬性的間接引用,可以用過 . (例如 user.name) 或者 [] (例如 user['name']) 的形式完成。
“追蹤函數” 是 computed 表達式、observer 組件的 render() 方法和 when、reaction 和 autorun 的第一個入參函數。
“過程(during)” 意味着只追蹤那些在函數執行時被讀取的 observable 。這些值是否由追蹤函數直接或間接使用並不重要。
MobX 追蹤屬性訪問,而不是值。
Mobx不會對可觀察狀態的改變作出反應舉例:
@observable message = { title: "Foo", author: { name: "Michel" }, likes: [ "John", "Sara" ] }
如上message對應的可觀察圖示:
在追蹤函數外進行間接引用
ar title = message.title; autorun(() => { console.log(title) }) message.title = "Bar"
MobX 只追蹤同步地訪問數據
autorun(() => { setTimeout( () => console.log(message.likes.join(", ")), 10 ) }) message.likes.push("Jennifer");
在 autorun 執行期間沒有訪問到任何 observable,而只在 setTimeout 執行期間訪問了。
MobX 只會為數據是直接通過 render 存取的 observer 組件進行數據追蹤
React中使用mobx,只會對render中直接存取的可觀察狀態進行跟蹤
像上述將值傳遞給子組件和將可觀察狀態值緩存到組件內是不會對狀態的變化有反應的。
3、改變狀態
動作是任何用來修改狀態的函數,且應該永遠只對修改狀態的函數使用動作。
在mobx中可以在任意位置修改狀態,但是推薦使嚴格模式,在嚴格模式下,只能在action中修改狀態。
Mobx提供action和@action包裝action函數
Mobx提供action.bound和@action.bound幫助bind this
@action classMethod() {}
@action(name) classMethod () {}
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}
用法舉例:
class commonStoreClass { @observable time = ''; @action updateTime(time){ this.time = time; } @action.bound computedTime(){ const nowTime=new Date(); const year=nowTime.getFullYear(); const month=nowTime.getMonth()+1; const date=nowTime.getDate(); const time = year+"年"+month+"月"+date+" "+nowTime.toLocaleTimeString(); this.updateTime(time); } @action startTime(){ this.timer = setInterval(this.computedTime,1000); } @action stopTime(){ clearInterval(this.timer); } }
函數內多個修改狀態的操作會被合並,全部修改完后會通知computed和reaction
應該永遠只對修改狀態的函數使用動作。 只執行查找,過濾器等函數不應該被標記為動作
推薦使嚴格模式,在嚴格模式下,只能在action中修改狀態。
注意在需要時bind this
4、各api的import
上面提到的api中與React相關的由mobx-react提供,其余的由mobx包提供。
import {observable, action, computed, autorun, reaction, when} from 'mobx';
import {Provider, inject, observer} from 'mobx-react';
Provider與inject的使用會在后面介紹。
---------------------
作者:smk108
來源:CSDN
原文:https://blog.csdn.net/smk108/article/details/84960159
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!