摘要:前言原本說接下來會(huì)專注學(xué)但是最新工作又學(xué)習(xí)了一些有意思的庫(kù)於是就再寫下來做個(gè)簡(jiǎn)單的入門之前我寫過一篇文章這個(gè)也算是作為一個(gè)補(bǔ)充吧這次無非就是類似筆記把認(rèn)為的一些關(guān)鍵點(diǎn)記下來有些地方還沒用到就衹是描述一下代碼有些自己寫的有些文檔寫的很好就搬下
前言
原本說接下來會(huì)專注學(xué)nodejs,但是最新工作又學(xué)習(xí)了一些有意思的庫(kù),於是就再寫下來做個(gè)簡(jiǎn)單的入門,之前我寫過一篇文章,這個(gè)也算是作為一個(gè)補(bǔ)充吧.
這次無非就是類似筆記,把認(rèn)為的一些關(guān)鍵點(diǎn)記下來,有些地方還沒用到就衹是描述一下,代碼有些自己寫的,有些文檔寫的很好就搬下來,想瞭解更多可看官網(wǎng)
Mobx中文文檔
Mobx英文文檔
Github 倉(cāng)庫(kù)
PS:
2018/06/11 發(fā)現(xiàn)中文版有些關(guān)鍵地方?jīng)]翻譯,補(bǔ)充一下observable({})/observable.object(props, decorators?, options?)部分
通過透明的響應(yīng)式編程使?fàn)顟B(tài)管理變得簡(jiǎn)單和可擴(kuò)展,背后哲學(xué)是任何源自應(yīng)用狀態(tài)的東西都應(yīng)自動(dòng)獲得,(包括UI,數(shù)據(jù)序列化,服務(wù)器通訊等)
React通過提供機(jī)制把應(yīng)用狀態(tài)轉(zhuǎn)換成可渲染組件樹并對(duì)其渲染,優(yōu)化UI渲染,就是通過使用虛擬DOM減少昂貴的DOM變化數(shù)量.
Mobx提供機(jī)制來存儲(chǔ)和更新應(yīng)用狀態(tài)供React使用,優(yōu)化應(yīng)用狀態(tài)和React組件同步,通過使用響應(yīng)式的虛擬依賴狀態(tài)圖表,讓其在需要的時(shí)候才更新并且保持最新.
個(gè)人感覺與Redux相比除了目的一致是管理應(yīng)用狀態(tài)之外.不管是寫法還是思想都截然不同.因?yàn)椴艅側(cè)腴T,這裡只說用法不講區(qū)別.
官方代碼我們先看看這段代碼做了什麼,再分開詳細(xì)講解一下對(duì)應(yīng)知識(shí)點(diǎn)
observable(組件): 轉(zhuǎn)成響應(yīng)式組件,會(huì)自動(dòng)轉(zhuǎn)換應(yīng)用狀態(tài)和更新;
get 函數(shù): 計(jì)算值,根據(jù)現(xiàn)有的狀態(tài)或其它計(jì)算值衍生出的值;
autorun函數(shù): 類似get,依賴關(guān)系改變時(shí)觸發(fā);
action: 改變狀態(tài),嚴(yán)格模式下所有修改操作都應(yīng)該在action裡面執(zhí)行;
import {observable, autorun} from "mobx"; var todoStore = observable({ /* 一些觀察的狀態(tài) */ todos: [], /* 推導(dǎo)值 */ get completedCount() { return this.todos.filter(todo => todo.completed).length; }, }); /* 觀察狀態(tài)改變的函數(shù) */ autorun(function() { console.log( "Completed %d of %d items", todoStore.completedCount, todoStore.todos.length ); }); /* ..以及一些改變狀態(tài)的動(dòng)作 */ todoStore.todos[0] = { title: "Take a walk", completed: false, }; // -> 同步打印 "Completed 0 of 1 items" todoStore.todos[0].completed = true; // -> 同步打印 "Completed 1 of 1 items"
官方流程圖:
//標(biāo)準(zhǔn)用法 observable(value) //裝飾器用法 @observable classProperty = value
Observable 值可以是JS基本數(shù)據(jù)類型、引用類型、普通對(duì)象、類實(shí)例、數(shù)組和映射. 匹配類型應(yīng)用了以下轉(zhuǎn)換規(guī)則,但可以通過使用調(diào)節(jié)器進(jìn)行微調(diào).
Map: 返回一個(gè)新的 Observable Map,不但對(duì)一個(gè)特定項(xiàng)的更改做出反應(yīng),而且對(duì)添加或刪除該項(xiàng)也做出反應(yīng);
數(shù)組: 會(huì)返回一個(gè) Observable Array;
沒有原型的對(duì)象: 那么對(duì)象會(huì)被克隆并且所有的屬性都會(huì)被轉(zhuǎn)換成可觀察的;
有原型的對(duì)象: JavaSript 原始數(shù)據(jù)類型或者函數(shù),observable會(huì)拋出錯(cuò)誤,如果你想要?jiǎng)?chuàng)建一個(gè)獨(dú)立的
observable引用例如值可以使用Boxed Observable observables.MobX 不會(huì)將一個(gè)有原型的對(duì)象自動(dòng)轉(zhuǎn)換成可觀察的,因?yàn)檫@是它構(gòu)造函數(shù)的職責(zé).可以在constructor使用extendObservable或者類型定義使用decorate替代.
observable(new Map())/observable.map(values, options)values: 可以是對(duì)象、 數(shù)組或者字符串鍵的 ES6 map;
options:
deep: 決定分配給 observable 映射的值會(huì)否通過 observable 來傳遞使其轉(zhuǎn)變成可觀察的;
name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
const map = observable.map(new Map());
以下是MobX 提供方法:
toJS(): 將 observable 映射轉(zhuǎn)換成普通映射;
toJSON(): 返回此映射的淺式普通對(duì)象表示.(想要深拷貝,請(qǐng)使用 mobx.toJS(map));
intercept(interceptor): 可以用來在任何變化作用于映射前將其攔截;
observe(listener, fireImmediately?): 注冊(cè)偵聽器,在映射中的每個(gè)更改時(shí)觸發(fā);
merge(values): 把提供對(duì)象的所有項(xiàng)拷貝到映射中.values 可以是普通對(duì)象、entries 數(shù)組或者 ES6 字符串鍵的映射;
replace(values): 用提供值替換映射全部?jī)?nèi)容.是 .clear().merge(values) 的簡(jiǎn)寫形式;
observable([])/observable.array(values, options)這是遞歸的,所以數(shù)組中的所有(未來的)值都會(huì)是可觀察的.
options:
deep: 決定分配給 observable 映射的值會(huì)否通過 observable 來傳遞使其轉(zhuǎn)變成可觀察的;
name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
const ary = observable.array([1, 2, 4]);
注意:
observable.array 會(huì)創(chuàng)建一個(gè)人造數(shù)組(類數(shù)組對(duì)象)來代替真正的數(shù)組. 支持所有的原生方法,包括從索引的分配到包含數(shù)組長(zhǎng)度.
驗(yàn)證類型方法的話返回不是數(shù)組.可以通過使用 array.slice() 在 observable 數(shù)組傳遞給外部庫(kù)或者內(nèi)置方法前創(chuàng)建一份淺拷貝;
sort 和 reverse 函數(shù)實(shí)現(xiàn)不會(huì)改變數(shù)組本身,而是返回一個(gè)排序過/反轉(zhuǎn)過的拷貝;
以下是MobX 提供方法:
intercept(interceptor): 可以用來在任何變化作用于數(shù)組前將其攔截;
observe(listener, fireImmediately? = false): 監(jiān)聽數(shù)組的變化.回調(diào)函數(shù)將接收表示數(shù)組拼接或數(shù)組更改的參數(shù),它符合 ES7 提議.它返回一個(gè)清理函數(shù)以用來停止監(jiān)聽器;
clear(): 從數(shù)組中刪除所有項(xiàng);
replace(newItems): 用新項(xiàng)替換數(shù)組中所有已存在的項(xiàng);
find(predicate: (item, index, array) => boolean, thisArg?): 基本上等同于 ES7 的 Array.find 提議;
findIndex(predicate: (item, index, array) => boolean, thisArg?): 基本上等同于 ES7 的 Array.findIndex 提議;
remove(value): 通過值從數(shù)組中移除一個(gè)單個(gè)的項(xiàng).如果項(xiàng)被找到并移除的話,返回 true ;
peek(): 和 slice() 類似,返回一個(gè)有所有值的數(shù)組并且數(shù)組可以放心的傳遞給其它庫(kù),但是不創(chuàng)建保護(hù)性拷貝;
observable({})/observable.object(props, decorators?, options?)一個(gè)普通的 JavaScript 對(duì)象 (指不是使用構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象,而是以 Object 作為其原型,或者根本沒有原型)傳遞給 observable 方法,對(duì)象的所有屬性都將被拷貝至一個(gè)克隆對(duì)象并將克隆對(duì)象轉(zhuǎn)變成可觀察的.
這是遞歸應(yīng)用的,所以如果對(duì)象的某個(gè)值是一個(gè)對(duì)象或數(shù)組,那么該值也將通過 observable 傳遞.
options:
deep: 決定分配給 observable 映射的值會(huì)否通過 observable 來傳遞使其轉(zhuǎn)變成可觀察的;
name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
const obj = observable.object({ key: "value"});
注意:
[MobX 4及以下]當(dāng)通過 observable 傳遞對(duì)象時(shí),只有在把對(duì)象轉(zhuǎn)變 observable 時(shí)存在的屬性才會(huì)是可觀察的. 稍后添加到對(duì)象的屬性不會(huì)變?yōu)榭捎^察的,除非使用 set 或 extendObservable;
只有普通的對(duì)象可以轉(zhuǎn)變成 observable .對(duì)于非普通對(duì)象,構(gòu)造函數(shù)負(fù)責(zé)初始化 observable 屬性. 要么使用 @observable 注解[color=#b1b1b1](annotation,這個(gè)解釋不太懂??),要么使用 extendObservable 函數(shù);
屬性的 getter 會(huì)自動(dòng)轉(zhuǎn)變成衍生屬性,就像 @computed 所做的;
observable 是自動(dòng)遞歸到整個(gè)對(duì)象的.在實(shí)例化過程中和將來分配給 observable 屬性的任何新值的時(shí)候.Observable 不會(huì)遞歸到非普通對(duì)象中;
更細(xì)粒度的控制,比如哪些屬性應(yīng)該轉(zhuǎn)變成可觀察的和如何變成可觀察的,請(qǐng)參見裝飾器;
observable.box(value)JavaScript 中的所有原始類型值都是不可變的,因此它們都是不可觀察的,box創(chuàng)建一個(gè)基于 ref 裝飾器的箱子.這意味著箱子里的任何(將來)值都不會(huì)自動(dòng)地轉(zhuǎn)換成 observable .
options:
1) name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
const box = observable.box("box"); box.observe(function(change) { console.log(change.oldValue, "->", change.newValue); });
以下是box提供方法:
get(): 返回當(dāng)前值;
set(value): 替換當(dāng)前存儲(chǔ)的值并通知所有觀察者;
intercept(interceptor): 可以用來在任何變化應(yīng)用前將其攔截;
observe(callback: (change) => void, fireImmediately = false): 注冊(cè)一個(gè)觀察者函數(shù),每次存儲(chǔ)值被替換時(shí)觸發(fā).返回一個(gè)函數(shù)以取消觀察者.change是一個(gè)對(duì)象,其中包含 observable 的 newValue 和 oldValue .
裝飾器定義 observable 屬性的行為,默認(rèn)為對(duì)任意鍵值對(duì)使用 observable.deep,對(duì) getters 使用 computed .
observable: observable.deep 的別名
observable.deep: 任何 observable 都使用的默認(rèn)的調(diào)節(jié)器.它將任何(尚未成為 observable )數(shù)組,映射或純對(duì)象克隆并轉(zhuǎn)換為 observable 對(duì)象,并將其賦值給給定屬性
observable.ref: 禁用自動(dòng)的 observable 轉(zhuǎn)換,只是創(chuàng)建一個(gè) observable 引用
observable.shallow: 只能與集合組合使用. 將任何分配的集合轉(zhuǎn)換為 observable,但該集合的值將按原樣處理
observable.struct: 就像 ref, 但會(huì)忽略結(jié)構(gòu)上等于當(dāng)前值的新值
computed: 創(chuàng)建一個(gè)衍生屬性, 參見 computed
computed(options): 同 computed , 可設(shè)置選項(xiàng)
computed.struct: 與 computed 相同,但是只有當(dāng)視圖產(chǎn)生的值與之前的值結(jié)構(gòu)上有不同時(shí),才通知它的觀察者
action: 創(chuàng)建一個(gè)動(dòng)作, 參見 action
action(name): 創(chuàng)建一個(gè)動(dòng)作,重載了名稱
action.bound: 創(chuàng)建一個(gè)動(dòng)作, 并將 this 綁定到了實(shí)例
class Person { name = "John"; } // 使用 decorate 時(shí),所有字段都應(yīng)該指定 (畢竟,類里的非 observable 字段可能會(huì)更多) decorate(Person, { name: observable, });Derivations(衍生)
任何源自狀態(tài)并且不會(huì)再有任何進(jìn)一步的相互作用的東西就是衍生,衍生以多種形式存在:
用戶界面
衍生數(shù)據(jù),比如剩下的待辦事項(xiàng)的數(shù)量.
后端集成,比如把變化發(fā)送到服務(wù)器端.
MobX 區(qū)分兩種類型的衍生:
Computed values(計(jì)算值): 它們是永遠(yuǎn)可以使用純函數(shù)(pure function)從當(dāng)前可觀察狀態(tài)中衍生出的值;
Reactions(反應(yīng)): Reactions 是當(dāng)狀態(tài)改變時(shí)需要自動(dòng)發(fā)生的副作用.需要有一個(gè)橋梁來連接命令式編程(imperative programming)和響應(yīng)式編程(reactive programming).或者說得更明確一些,它們最終都需要實(shí)現(xiàn)I / O 操作;
(@)computed計(jì)算值(computed values)是可以根據(jù)現(xiàn)有的狀態(tài)或其它計(jì)算值衍生出的值.如果你想響應(yīng)式的產(chǎn)生一個(gè)可以被其它 observer 使用的值,請(qǐng)使用 @computed.計(jì)算值在大多數(shù)情況下可以被 MobX 優(yōu)化的,例如:
前一個(gè)計(jì)算中使用的數(shù)據(jù)沒有更改,計(jì)算屬性將不會(huì)重新運(yùn)行;
某個(gè)其它計(jì)算屬性或 reaction 未使用該計(jì)算屬性,也不會(huì)重新運(yùn)行. 在這種情況下,它將被暫停;
一個(gè)計(jì)算值不再被觀察了,例如使用它的UI不復(fù)存在了,MobX 可以自動(dòng)地將其垃圾回收;
注意:
計(jì)算屬性是不可枚舉的,它們也不能在繼承鏈中被覆蓋;
可以使用 observe 或 keepAlive 來強(qiáng)制保持計(jì)算值總是處于喚醒狀態(tài);
observable.object 和 extendObservable 都會(huì)自動(dòng)將 getter 屬性推導(dǎo)成計(jì)算屬性;
如果計(jì)算值在其計(jì)算期間拋出異常,則此異常將捕獲并在讀取其值時(shí)重新拋出. 強(qiáng)烈建議始終拋出“錯(cuò)誤”,以便保留原始堆棧跟蹤. 拋出異常不會(huì)中斷跟蹤,所有計(jì)算值可以從異常中恢復(fù).
計(jì)算值的 setter:
不能用來直接改變計(jì)算屬性的值,但是它們可以用來作“逆向”衍生.就是反向計(jì)算;
必須在 getter 之后定義 setter,一些 TypeScript 版本會(huì)知道聲明了兩個(gè)具有相同名稱的屬性;
這是一個(gè)自動(dòng)的動(dòng)作,只需要直接使用set xx(){};
class Test { @observable num = 0; @computed get total() { return this.num * 10; } set total(value) { this.num = value / 10; } } //OR const Test = observable.object({ num: 0; get total() { return this.num * 10; } set total(value) { this.num = value / 10; } })computed(expression,options) 函數(shù)用法
某些情況下,你需要傳遞一個(gè)“在box中”的計(jì)算值時(shí),它可能是有用的.
options:
name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
context: 在提供的表達(dá)式中使用的 this;
set: 要使用的setter函數(shù). 沒有 setter 的話無法為計(jì)算值分配新值. 如果傳遞給 computed 的第二個(gè)參數(shù)是一個(gè)函數(shù),那么就把會(huì)這個(gè)函數(shù)作為 setter;
equals: 默認(rèn)值是 comparer.default .它充當(dāng)比較函數(shù).如果前后值相等,那么觀察者就不會(huì)重新評(píng)估;
requiresReaction: 對(duì)于非常昂貴的計(jì)算值,推薦設(shè)置成 true .如果你嘗試讀取它的值,但某些觀察者沒有跟蹤該值(在這種情況下,MobX 不會(huì)緩存該值),則會(huì)導(dǎo)致計(jì)算結(jié)果丟失,而不是進(jìn)行昂貴的重新評(píng)估;
keepAlive: 如果沒有任何人觀察到,則不要使用此計(jì)算值. 請(qǐng)注意,這很容易導(dǎo)致內(nèi)存泄漏,因?yàn)樗鼤?huì)導(dǎo)致此計(jì)算值使用的每個(gè) observable ,并將計(jì)算值保存在內(nèi)存中;
MobX 提供了三個(gè)內(nèi)置 comparer (比較器) :
comparer.identity: 使用恒等 (===) 運(yùn)算符來判定兩個(gè)值是否相同;
comparer.default: 等同于 comparer.identity,但還認(rèn)為 NaN 等于 NaN ;
comparer.structural: 執(zhí)行深層結(jié)構(gòu)比較以確定兩個(gè)值是否相同;
const box = observable("box"), upperCaseName = computed(() => name.get().toUpperCase()), disposer = upperCaseName.observe(change => console.log(change.newValue)); box.set("Dave");Autorun(expression,options)
創(chuàng)建一個(gè)響應(yīng)式函數(shù),而該函數(shù)本身永遠(yuǎn)不會(huì)有觀察者,調(diào)用后將接收一個(gè)參數(shù),即當(dāng)前 reaction(autorun),可用于在執(zhí)行期間清理 autorun. 當(dāng)使用 autorun 時(shí),所提供的函數(shù)總是立即被觸發(fā)一次,然后每次它的依賴關(guān)系改變時(shí)會(huì)再次被觸發(fā),相比之下computed(function)創(chuàng)建的函數(shù)只有當(dāng)它有自己的觀察者時(shí)才會(huì)重新計(jì)算,否則它的值會(huì)被認(rèn)為是不相關(guān)的;
options:
delay: 可用于對(duì)效果函數(shù)進(jìn)行去抖動(dòng)的數(shù)字(以毫秒為單位).如果是 0(默認(rèn)值) 的話,那么不會(huì)進(jìn)行去抖.
name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
onError: 用來處理 reaction 的錯(cuò)誤,而不是傳播它們;
scheduler: 設(shè)置自定義調(diào)度器以決定如何調(diào)度 autorun 函數(shù)的重新運(yùn)行;
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(); //清理 autorun numbers.push(5); // 不會(huì)再輸出任何值.`sum` 不會(huì)再重新計(jì)算.when(predicate: () => boolean, effect?: () => void, options?)
when 觀察并運(yùn)行給定的 predicate,直到返回true. 一旦返回 true,給定的 effect 就會(huì)被執(zhí)行,然后 autorunner(自動(dòng)運(yùn)行程序) 會(huì)被清理. 該函數(shù)返回一個(gè)清理器以提前取消自動(dòng)運(yùn)行程序.
class MyResource { constructor() { when( // 一旦... () => !this.isVisible, // ... 然后 () => this.dispose() ); } @computed get isVisible() { // 標(biāo)識(shí)此項(xiàng)是否可見 } dispose() { // 清理 } }when-promise
如果沒提供 effect 函數(shù),when 會(huì)返回一個(gè) Promise .它與 async / await 可以完美結(jié)合.
async function() { await when(() => that.isVisible) // 等等.. }reaction(() => data, (data, reaction) => { sideEffect }, options?)
第一個(gè)數(shù)據(jù)函數(shù)是用來追蹤并返回?cái)?shù)據(jù)作為第二個(gè)作用函數(shù)的入?yún)? 不同于 autorun 的是當(dāng)創(chuàng)建時(shí)函數(shù)不會(huì)直接運(yùn)行,只有在數(shù)據(jù)表達(dá)式首次返回一個(gè)新值后才會(huì)運(yùn)行. 在執(zhí)行作用函數(shù)時(shí)訪問的任何 observable 都不會(huì)被追蹤.
reaction 返回一個(gè)清理函數(shù),接收兩個(gè)參數(shù),即當(dāng)前的 reaction,可以用來在執(zhí)行期間清理 reaction .
options:
fireImmediately: 布爾值,用來標(biāo)識(shí)效果函數(shù)是否在數(shù)據(jù)函數(shù)第一次運(yùn)行后立即觸發(fā).默認(rèn)值是 false,如果一個(gè)布爾值作為傳給 reaction 的第三個(gè)參數(shù),那么它會(huì)被解釋為 fireImmediately 選項(xiàng);
delay: 可用于對(duì)效果函數(shù)進(jìn)行去抖動(dòng)的數(shù)字(以毫秒為單位).如果是 0(默認(rèn)值) 的話,那么不會(huì)進(jìn)行去抖;
equals: 默認(rèn)值是 comparer.default .它充當(dāng)比較函數(shù).如果前后值相等,那么觀察者就不會(huì)重新評(píng)估;
name: 調(diào)試名稱,用于 spy 或者 MobX 開發(fā)者工具;
onError: 用來處理 reaction 的錯(cuò)誤,而不是傳播它們;
scheduler: 設(shè)置自定義調(diào)度器以決定如何調(diào)度 autorun 函數(shù)的重新運(yùn)行:
const todos = observable([ { title: "test1" }, { title: "test2" } ]); //會(huì)對(duì)長(zhǎng)度變化作出反應(yīng) reaction(() => todos.length, length => console.log("reaction 1:", todos.map(todo => todo.title).join(", "))); //會(huì)對(duì)某個(gè) todo 的 title 變化作出反應(yīng) reaction(() => todos.map(todo => todo.title), titles => console.log("reaction 2:", titles.join(", "))); // autorun 對(duì)它函數(shù)中使用的任何東西作出反應(yīng) autorun(() => console.log("autorun:", todos.map(todo => todo.title).join(", "))); // 輸出: // autorun: test1 action(() => { todos.push({title: "test3"}); })() // 輸出: // autorun: test1, test2, test3 // reaction 2: test1, test2, test3 // reaction 1: test1, test2, test3 action(() => { todos[1].title = "test4"; })() // 輸出: // autorun: test1, test4, test3 // reaction 2: test1, test4, test3@observer
observer 函數(shù)/裝飾器可以用來將 React 組件/無狀態(tài)函數(shù)組件轉(zhuǎn)變成響應(yīng)式組件. 它用 mobx.autorun 包裝了組件的 render 函數(shù)以確保任何組件渲染中使用的數(shù)據(jù)變化時(shí)都可以強(qiáng)制刷新組件.
observer 是由多帶帶的 mobx-react 包提供的.確保 observer 是最深處(第一個(gè)應(yīng)用)的裝飾器,否則它可能什么都不做;
如果傳遞給組件的數(shù)據(jù)是響應(yīng)式的,observer還可以防止當(dāng)組件的 props 只是淺改變時(shí)的重新渲染,這個(gè)行為與 React PureComponent 相似,不同在于這里的 state 的更改仍然會(huì)被處理. 如果一個(gè)組件提供了它自己的 shouldComponentUpdate,這個(gè)方法會(huì)被優(yōu)先調(diào)用;
import {observer} from "mobx-react"; var timerData = observable({ secondsPassed: 0 }); setInterval(() => { timerData.secondsPassed++; }, 1000); @observer class Timer extends React.Component { render() { return (Seconds passed: { this.props.timerData.secondsPassed } ) } }; //OR const Timer = observer(({ timerData }) => Seconds passed: { timerData.secondsPassed } );可觀察的局部組件狀態(tài)
@observer class Timer extends React.Component {
@observable secondsPassed = 0
componentWillMount() {
setInterval(() => {
this.secondsPassed++
}, 1000)
}
render() {
return (Seconds passed: { this.secondsPassed } )
}
}
在React組件上引入可觀察屬性. 這意味著你可以在組件中擁有功能同樣強(qiáng)大的本地狀態(tài)(local state),而不需要通過 React 的冗長(zhǎng)和強(qiáng)制性的 setState 機(jī)制來管理. 響應(yīng)式狀態(tài)會(huì)被 render 提取調(diào)用,但不會(huì)調(diào)用其它 React 的生命周期方法,除了 componentWillUpdate 和 componentDidUpdate . 如果你需要用到其他 React 生命周期方法 ,只需使用基于 state 的常規(guī) React API 即可.
inject組件連接storesconst App = () =>提供生命週期componentWillReact; const Button = inject("colors")(observer(({ colors, label }) =>
當(dāng)組件重新渲染時(shí)被觸發(fā),這使得它很容易追溯渲染并找到導(dǎo)致渲染的操作(action).
不接收參數(shù);
初始化渲染前不會(huì)觸發(fā) (使用 componentWillMount 替代);
對(duì)于 mobx-react@4+, 當(dāng)接收新的 props 時(shí)并在 setState 調(diào)用后會(huì)觸發(fā)此鉤子;
action (動(dòng)作)action(fn)
action(name, fn)
@action classMethod() {}
@action(name) classMethod () {}
@action boundClassMethod = (args) => { body }
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}
action可以是任何用來修改狀態(tài)的東西,只執(zhí)行查找,過濾器等函數(shù)不應(yīng)該被標(biāo)記為action,以允許 MobX 跟蹤它們的調(diào)用.可以有助于更好的組織代碼.
action.bound自動(dòng)地將動(dòng)作綁定到目標(biāo)對(duì)象.與 action 不同的是不需要一個(gè)name參數(shù),名稱將始終基于動(dòng)作綁定的屬性.
因?yàn)榧^函數(shù)已經(jīng)是綁定過的并且不能重新綁定,所以不能一起使用
class Ticker { @observable tick = 0 @action.bound increment() { this.tick++ // "this" 永遠(yuǎn)都是正確的 } }編寫異步 Actions
action 包裝/裝飾器只會(huì)對(duì)當(dāng)前運(yùn)行的函數(shù)作出反應(yīng),而不會(huì)對(duì)當(dāng)前運(yùn)行函數(shù)所調(diào)用的函數(shù)(不包含在當(dāng)前函數(shù)之內(nèi))作出反應(yīng)! 這意味著如果 action 中存在 setTimeout、promise 的 then 或 async 語(yǔ)句,并且在回調(diào)函數(shù)中某些狀態(tài)改變了,那么這些回調(diào)函數(shù)也應(yīng)該包裝在 action 中.
錯(cuò)誤寫法,拋出異常
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action fetchProjects() { this.status = "yellow" toggleLight().then( res => { this.status = "green" }, err => { this.status = "red" } ) } }
//包裝修復(fù)在action
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax() { this.status = "yellow" toggleLight().then( this.handleSuc, this.handleErr ) } @action.bound handleSuc(res){ this.status = "green" } @action.bound handleErr(err){ this.status = "red" } }
//另一種內(nèi)嵌寫法
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax() { this.status = "yellow" toggleLight().then( action("handleSuc",res => { this.status = "green" }), action("handleErr",res => { this.status = "red" }) ) } }
runInAction(name?, thunk)
runInAction 是個(gè)簡(jiǎn)單的工具函數(shù),它接收代碼塊并在(異步的)動(dòng)作中執(zhí)行.這對(duì)于即時(shí)創(chuàng)建和執(zhí)行動(dòng)作非常有用.
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax() { this.status = "yellow" toggleLight().then( runInAction(res => { this.status = "green" }), runInAction(res => { this.status = "red" }) ) } }
async / await
async / await 只是圍繞基于 promise 過程的語(yǔ)法糖. 結(jié)果是 @action 僅應(yīng)用于代碼塊,直到第一個(gè) await . 在每個(gè) await 之后,一個(gè)新的異步函數(shù)將啟動(dòng),所以在每個(gè) await 之后,狀態(tài)修改代碼應(yīng)該被包裝成動(dòng)作.
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action async handleAjax() { this.status = "yellow" toggleLight().then( try{ const result = await dosometings(); runInAction(res => { this.status = result; }), }catch(err){ runInAction(res => { this.status = "red"; }) } ) } }
flow內(nèi)置概念
優(yōu)點(diǎn)是它在語(yǔ)法上基本與 async / await 是相同的 (只是關(guān)鍵字不同),并且不需要手動(dòng)用 @action 來包裝異步代碼,這樣代碼更簡(jiǎn)潔.
class TrafficLight { @observable status = "yellow" // "red" / "green" / "yellow" @action handleAjax = flow(function* () { this.status = "yellow" toggleLight().then( try{ const result = yield dosometings(); this.status = result; }catch(err){ this.status = "red"; } ) }) }工具 API
這些 API 都是響應(yīng)式的,這意味著如果使用 set 進(jìn)行添加,使用 values 或 keys 進(jìn)行迭代,即便是新屬性的聲明都可以被 MobX 檢測(cè)到.
values(thing): 將集合中的所有值作為數(shù)組返回;
keys(thing): 將集合中的所有鍵作為數(shù)組返回;
set(thing, key, value)/set(thing, { key: value }): 使用提供的鍵值對(duì)來更新給定的集合;
remove(thing, key): 從集合中移除指定的項(xiàng).用于數(shù)組拼接;
has(thing, key): 如果集合中存在指定的 observable 屬性就返回 true;
get(thing, key): 返回指定鍵下的子項(xiàng);
import { get, set, observable, values } from "mobx" const twitterUrls = observable.object({ "John": "twitter.com/johnny" }) autorun(() => { console.log(get(twitterUrls, "Sara")) // get 可以追蹤尚未存在的屬性 }) autorun(() => { console.log("All urls: " + values(twitterUrls).join(", ")) }) set(twitterUrls, { "Sara" : "twitter.com/horsejs"})Mobx工具函數(shù)
不想摘抄了,看文檔吧...
Mobx工具函數(shù)
不想摘抄了,看文檔吧...
Mobx貼士與技巧
(更多內(nèi)容請(qǐng)自行查閱,本節(jié)到此為止了.)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/106473.html
摘要:將注意力集中保持在核心庫(kù),而將其他功能如路由和全局狀態(tài)管理交給相關(guān)的庫(kù)。此示例使用類似的語(yǔ)法,稱為。執(zhí)行更快,因?yàn)樗诰幾g為代碼后進(jìn)行了優(yōu)化。基于的模板使得將已有的應(yīng)用逐步遷移到更為容易。 前言 因?yàn)闆]有明確的界定,這里不討論正確與否,只表達(dá)個(gè)人對(duì)前端MV*架構(gòu)模式理解看法,再比較React和Vue兩種框架不同.寫完之后我知道這文章好水,特別是框架對(duì)比部分都是別人說爛的,而我也是打算把...
摘要:它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。寫需要的組件創(chuàng)建一個(gè)組件,懟下面的內(nèi)容,你喜歡彬哥哪一點(diǎn)創(chuàng)建一個(gè)展示組件我喜歡彬哥打開,刪掉沒用的東西,直接懟下面的代碼,到這里,架子就搭好了。 通過本文你將: 1.知道什么是Vuex. 2.知道為什么要用Vuex. 3.能跑一個(gè)Vuex的例子。 4.了解相關(guān)概念,面試的時(shí)候能說出一個(gè)所以然 5...
摘要:它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。這需要對(duì)短期和長(zhǎng)期效益進(jìn)行權(quán)衡。改變中的狀態(tài)的唯一途徑就是顯式地提交。在里使用進(jìn)行延遲執(zhí)行。上下文對(duì)象,這里你可以理解稱本身。 這篇文章主要介紹了Vuex入門到上手教程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧 一、前言 當(dāng)我們的應(yīng)用遇到多個(gè)組件共享狀態(tài)時(shí),會(huì)需...
閱讀 1010·2021-11-22 13:52
閱讀 924·2019-08-30 15:44
閱讀 570·2019-08-30 15:43
閱讀 2424·2019-08-30 12:52
閱讀 3473·2019-08-29 16:16
閱讀 637·2019-08-29 13:05
閱讀 2943·2019-08-26 18:36
閱讀 1975·2019-08-26 13:46