新聞中心
mobx 是流行的狀態(tài)管理庫,熱度僅次于 redux。它和 redux 有的地方一樣,也有的地方不一樣:

一樣的地方是 mobx 和 redux 都是單向數(shù)據(jù)流,通過 action 觸發(fā)全局 state 更新,然后通知視圖。
redux 的數(shù)據(jù)流:
mobx 的數(shù)據(jù)流:
但是它們修改狀態(tài)的方式不一樣:
redux 是每次返回一個(gè)全新的狀態(tài),一般搭配實(shí)現(xiàn)對(duì)象 immutable 的庫來用。
mobx 每次都是修改的同一個(gè)狀態(tài)對(duì)象,基于響應(yīng)式代理,也就是 Object.defineProperty 代理 get、set 的處理,get 時(shí)把依賴收集起來,set 修改時(shí)通知所有的依賴做更新。和 vue2 的響應(yīng)式代理很類似。
其中,redux 那種方式是函數(shù)式的思路,所以狀態(tài)的修改都在一個(gè)個(gè) reducer 函數(shù)里,而 mobx 那種方式則是面向?qū)ο蟮拇淼乃悸?,所以很容易?state 組織成一個(gè)個(gè) class。
這也就導(dǎo)致了兩種狀態(tài)管理方式的代碼組織是有區(qū)別的:
redux 是在 reducer 函數(shù)里組織狀態(tài)(函數(shù)式的特點(diǎn)):
const reducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
};而 mobx 則是在 class 里組織狀態(tài)(面向?qū)ο蟮奶攸c(diǎn)):
import {observable, action} from 'mobx';
class Store {
@observable number = 0;
@action add = () => {
this.number++;
}
}此外,redux 那種方式每次都要返回一個(gè)新的對(duì)象,雖然可以用 immutable 的庫來減少創(chuàng)建新對(duì)象的開銷,但是比起 mobx 直接修改原對(duì)象來說,開銷還是大一點(diǎn)。
而且 redux 通知依賴更新的時(shí)候是全部通知的,而 mobx 因?yàn)槭占嗣總€(gè)屬性的依賴,可以精準(zhǔn)的通知。
所以 mobx 的性能會(huì)比 redux 高一些。
綜上,mobx 和 redux 都是單向數(shù)據(jù)流,但是管理狀態(tài)的思路上,一個(gè)是函數(shù)式的思想,通過 reducer 函數(shù)每次返回新的 state,一個(gè)是面向?qū)ο蟮乃枷?,通過響應(yīng)式對(duì)象來管理狀態(tài),這導(dǎo)致了狀態(tài)組織方式上的不同(function/class),而且 redux 創(chuàng)建新的 state 的開銷還有通知所有依賴的開銷都比 mobx 大,性能比 mobx 差一些。
對(duì)比下來,我們會(huì)發(fā)現(xiàn) mobx 似乎比 redux 優(yōu)秀一些。那我們具體看下 mobx 怎么用吧:
mobx 的使用
官方提供的 demo 是這樣的:
import React from "react"
import ReactDOM from 'react-dom';
import { makeAutoObservable } from "mobx"
import { observer } from "mobx-react"
class Timer {
secondsPassed = 0
constructor() {
makeAutoObservable(this)
}
increase() {
this.secondsPassed += 1
}
reset() {
this.secondsPassed = 0
}
}
const myTimer = new Timer()
const TimerView = observer(({ timer }) => (
))
setInterval(() => {
myTimer.increase()
}, 1000);
ReactDOM.render(, document.getElementById('root'));
就像前面說的,mobx 基于響應(yīng)式對(duì)象來管理狀態(tài),所以組織狀態(tài)是用 class 的形式。
我們聲明了 Timer 的 class,有一個(gè)屬性是 secondsPassed 代表過去了幾秒,有兩個(gè)方法來修改它。
在構(gòu)造器里調(diào)用 makeAutoObservable 來創(chuàng)建響應(yīng)式的代理。
然后 new 一個(gè) Timer 的對(duì)象,傳到組件里,組件使用 observer 的高階組件包裹,它負(fù)責(zé)把被包裹的組件添加到 timer 的響應(yīng)式依賴中去。
然后把這個(gè)組件渲染到 dom。
這樣就完成了 mobx 和 react 的結(jié)合使用,看下效果:
我們是把時(shí)間(secondsPassed)放在 mobx 的全局 state 中管理的,在組件里使用,然后定時(shí)更新它。發(fā)現(xiàn)每次更新組件都得到了通知并做了渲染,這就是全局狀態(tài)管理的功能。
demo 里我們用的 makeAutoObservable 函數(shù),它會(huì)自動(dòng)給屬性添加響應(yīng)式代理,方法會(huì)添加一層觸發(fā) action 的代理。
也可以手動(dòng)標(biāo)識(shí):
import { observable, action } from "mobx"
class Timer {
@observable secondsPassed = 0
constructor() {
}
@action increase() {
this.secondsPassed += 1
}
@action reset() {
this.secondsPassed = 0
}
}我們大概知道了 mobx 怎么用,那它是怎么實(shí)現(xiàn)的呢?
接下來我們從源碼來理一下它的實(shí)現(xiàn)原理:
mobx 的實(shí)現(xiàn)原理
首先,mobx 會(huì)對(duì)對(duì)象做響應(yīng)式代理,那代理以后的對(duì)象是什么樣的呢?
我們打印下:
原始對(duì)象的 secondsPassed 屬性是 0,increase 和 reset 方法體修改 secondsPassed 的值。
而代理以后的對(duì)象屬性分為了 get 和 set,并且實(shí)現(xiàn)變成了 this[$mobx].getObservablePropValue 和 setObservablePropValue,這明顯是做了響應(yīng)式的處理了。
代理以后的方法都變成了 excuteAction,執(zhí)行方法會(huì) dispatch 一個(gè) acition。
那這個(gè)響應(yīng)式的代理是怎么實(shí)現(xiàn)的呢?
跟蹤 makeAutoObservable 的源碼會(huì)發(fā)現(xiàn) mobx 創(chuàng)建了一個(gè) ObservableObjectAdministration 的對(duì)象放到了 $mobx 屬性上。
在 timer 對(duì)象確實(shí)是有這個(gè)屬性的:
用 Symbol 聲明了一個(gè)私有屬性 mobx administration 來放 ObservableObjectAdministration 對(duì)象。
然后還用 Symbol 聲明了一個(gè)私有屬性。mobx-keys 來放所有做了代理的屬性和方法名。
那這個(gè) ObservableObjectAdministration 對(duì)象是干啥的呢?
看下它的定義:
可以看到它有 values 屬性記錄每個(gè) key 的依賴。
還有 getObservableValue 和 setObservableValue 來獲取和設(shè)置某個(gè) key 的值。這兩個(gè)方法就是被代理后的屬性的 get set 最終調(diào)用的方法:
這不就串起來了么:
創(chuàng)建對(duì)象的時(shí)候 mobx 會(huì)對(duì)屬性和方法做代理,會(huì)添加一個(gè) Symbol(mobx administrator) 屬性到對(duì)象上來保存 ObservableObjectAdministration 對(duì)象,它是用來記錄屬性的所有依賴的,對(duì)屬性的 get 和 set 都會(huì)被代理到這個(gè) ObservableObjectAdministration 的 getXxx 和 setXxx 方法上。
我們打印下這個(gè)對(duì)象看看:
確實(shí),values 里保存了唯一一個(gè)屬性和它的所有依賴。
至此,對(duì)對(duì)象做響應(yīng)式代理的流程我們已經(jīng)理清了:
那這個(gè)依賴是什么時(shí)候收集的呢?
我們繼續(xù)往下看 get 收集依賴和 set 觸發(fā)依賴更新的部分:
我們用 observable 包裹了組件,它是一個(gè)高階組件,對(duì)組件做一層代理,返回新的組件:
在這層代理里面,創(chuàng)建了 Reaction 對(duì)象,也就是收到更新的通知之后怎么做出反應(yīng),在回調(diào)函數(shù)里用 setState([]) 的方式實(shí)現(xiàn)了強(qiáng)制更新。
并且,這層高階組件的代理里會(huì)把當(dāng)前組件設(shè)置到全局,這樣后面做 get 的依賴收集的時(shí)候就能拿到對(duì)應(yīng)的組件了。
所以在組件里用到 state 的 get,做依賴收集時(shí),就知道當(dāng)前是哪個(gè)組件了:
當(dāng)然,這里收集的不是具體哪個(gè)組件,而是 onInvalidate 的回調(diào)函數(shù),也就是收到更新的通知之后如何做出反應(yīng)。
這樣就完成了依賴的收集,在后面修改響應(yīng)式對(duì)象的狀態(tài)屬性的時(shí)候,就會(huì)觸發(fā)依賴,然后實(shí)現(xiàn)組件的更新:
這樣,我們就串聯(lián)起了 mobx 的響應(yīng)式原理:
總結(jié)
mobx 是熱度僅次于 redux 的狀態(tài)管理庫,它和 redux 有相同的地方也有不同的地方:
相同的地方是都是單向數(shù)據(jù)流。
不同的地方是 redux 是函數(shù)式思想的實(shí)現(xiàn),通過 reducer 函數(shù)管理狀態(tài),一般會(huì)用 immutable 的庫來提高創(chuàng)建新對(duì)象的性能。而 mobx 是面向?qū)ο蟮乃枷?,通過響應(yīng)式代理來管理狀態(tài),可以通過 class 組織 state。
性能方面 mobx 的響應(yīng)式能精準(zhǔn)的通知依賴做更新,而 redux 只能全局通知,而且 mobx 只是修改同一個(gè)對(duì)象,不是每次創(chuàng)建新對(duì)象,性能會(huì)比 redux 更高。
然后我們又通過一個(gè) demo 來入門了下 react 中使用 mobx:通過 class 組織狀態(tài),然后創(chuàng)建響應(yīng)式代理,組件用 observer 高階組件做一層包裝,傳入 mobx 的對(duì)象,這樣 mobx 和組件就結(jié)合到了一起,狀態(tài)更新就能通知到組件。
之后我們從源碼層面理清了 mobx 的響應(yīng)式機(jī)制的實(shí)現(xiàn)原理:mobx 會(huì)在對(duì)象上添加一個(gè) Symbol($mobx) 的隱藏屬性,用來放 ObservableObjectAdministration 對(duì)象,它是用于管理屬性和它的依賴的,在 get 的 時(shí)候收集依賴,然后 set 的時(shí)候就可以通知所有收集到的依賴(Reaction)做更新。
看到這里,你是否對(duì) mobx 的特點(diǎn)和原理有更深的理解了呢?
標(biāo)題名稱:一篇帶你揭秘MobX實(shí)現(xiàn)原理
網(wǎng)頁URL:http://www.5511xx.com/article/dpcjegc.html


咨詢
建站咨詢
