使用一種工具就像使用一個包裝好的黑盒子吃挑,我們不必探究其內(nèi)部到底是如何實現(xiàn)钝荡,只需要能夠?qū)⒂梅巳挥谛兀裁礃拥妮斎霑玫绞裁礃拥妮敵瞿苡型昝赖念A(yù)測儒鹿。但不幸的事化撕,大部分文檔都難以讓自己達(dá)到這點,為此我們不得不淺析一下源碼约炎,來探尋他是如何實現(xiàn)的植阴,避免出現(xiàn)意料之外的情況導(dǎo)致bug,也防止做出多余的操作圾浅,精簡代碼掠手,減少bug。
自動追蹤依賴
在使用redux的時候狸捕,我們不得不使用connect來從store中取到當(dāng)前組件所需要的state喷鸽,這其實也是一個依賴分析的問題,只有當(dāng)組件所依賴的state變化時灸拍,當(dāng)前組件才會更新做祝,避免了不必要的render砾省。而在使用mobx時,這一步被自動完成了混槐,因此在使用mobx時编兄,會感到極其酸爽舒適,只需要一個@observer就能盡情地使用store中的state了声登,而且完全不用擔(dān)心性能問題狠鸳,那么這究竟是如何做到的?
了解mobx機制
在探究mobx-react的源碼之前悯嗓,得先了解一下mobx件舵。
import { Reaction, observable } from 'mobx';
const obj = observable({ num: 0 });
const reaction = new Reaction('reaction name', function () {
this.track(() => {
console.log('track num++', obj.num);
});
});
reaction.track(() => {
console.log('reaction init', obj.num);
});
// 輸出 reaction init 0
obj.num++; // 輸出 track num++ 1
obj.num++; // 輸出 track num++ 2
mobx在observable改造了{ num: 0 }
這個對象為可觀察對象,當(dāng)取obj的屬性num時脯厨,能會執(zhí)行其內(nèi)部的方法铅祸。
reaction的track方法傳進(jìn)去的回調(diào)中,執(zhí)行console.log('reaction init', obj.num)
時俄认,get到了obj.num值个少,這時mobx會將這個屬性作為依賴與此reaction綁定,當(dāng)obj.num變化時眯杏,就會執(zhí)行實例化reaction時傳進(jìn)去進(jìn)去的回調(diào)
function () {
this.track(() => {
console.log('track num++', obj.num);
});
}
這時會輸出track num++ 1夜焦,并從新確定依賴關(guān)系。接下來看下一個例子
import { Reaction, observable } from 'mobx';
const obj = observable({ num: 0, letter: 'a', bool: true });
const callback = function () {
if (obj.bool) {
console.log(obj.num);
} else {
console.log(obj.letter);
}
};
const reaction = new Reaction('reaction name', function () {
this.track(callback);
});
reaction.track(callback); // 第一次track岂贩,分析到依賴[obj.bool, obj.num]茫经,輸出 0
obj.num++; // 第二次track,分析到依賴依然是[obj.bool, obj.num]萎津,輸出 1
obj.letter = 'b'; // obj.letter不在依賴中卸伞,不執(zhí)行this.track(callback)
obj.bool = false; // obj.bool在依賴中岩梳,執(zhí)行this.track(callback);重新分析依賴[obj.bool, obj.letter] 輸出 b
obj.num++; // 此時obj.num不在依賴中欲间,不輸出任何值
obj.letter = 'c'; // 此時obj.letter在依賴中,輸出 c
由此可見烘绽,mobx可以在每次執(zhí)行時會分析依賴颈渊,并且將在callback中遂黍,卻并不執(zhí)行的屬性排除在依賴外,減少了callback俊嗽。
看到這里雾家,我們應(yīng)該能想到,只要將callback當(dāng)成react的render绍豁,就能完美地解決對react的對接芯咧,并且比使用redux的時候性能更好!因為使用redux的時候?qū)⒁蕾噉um,letter敬飒,bool三個屬性邪铲,無論哪個改變都講觸發(fā)組件的render,除非根據(jù)業(yè)務(wù)邏輯寫上復(fù)雜的componentShouldUpdate驶拱,而使用mobx時霜浴,只要其中某個state的改變不影響view的改變時晶衷,這個state的變化就不會引起組件render蓝纲,達(dá)到更細(xì)粒度上對render的控制,近乎完美地排除不需要的render晌纫。
mobx-react源碼片段探究
那么接下來看一看mobx-react的源碼是如何實現(xiàn)的
// 使用mobx-react
import React, { Component} from 'react';
import { observer } from 'mobx-react';
@observer
export default MyCom extends Component {
render() {
// ...do something
}
}
// mobx-react的源碼片段
// ...
const baseRender = this.render.bind(this) // 組件自己的render就是baseRender
let reaction = null
let isRenderingPending = false
// 定義initialRender税迷,用來代替組件第一次render
const initialRender = () => {
reaction = new Reaction(`${initialName}#${rootNodeID}.render()`, () => {
if (!isRenderingPending) {
// 防止在constructor中修改store觸發(fā)引起的副作用(此時還沒有初始render)
isRenderingPending = true
// 觸發(fā)mobx-react添加的一個生命周期
if (typeof this.componentWillReact === "function") this.componentWillReact()
// 防止componentWillRect引起的副作用
if (this.__$mobxIsUnmounted !== true) {
let hasError = true
try {
isForcingUpdate = true
// skipRender是用來防止死循環(huán),這里不用管
// 當(dāng)依賴的state有變化是會使用forceUpdate強制render
// 并解析新的依賴
if (!skipRender) Component.prototype.forceUpdate.call(this)
hasError = false
} finally {
isForcingUpdate = false
if (hasError) reaction.dispose()
}
}
}
})
reaction.reactComponent = this
reactiveRender.$mobx = reaction
// 用reactRender替代render
this.render = reactiveRender
// 執(zhí)行reactiveRender
return reactiveRender()
}
const reactiveRender = () => {
isRenderingPending = false
let exception = undefined
let rendering = undefined
reaction.track(() => {
if (isDevtoolsEnabled) {
this.__$mobRenderStart = Date.now()
}
try {
// 允許state改變锹漱,然后執(zhí)行baseRedner箭养,這里將解析一次render中對state屬性的依賴
rendering = extras.allowStateChanges(false, baseRender)
} catch (e) {
exception = e
}
if (isDevtoolsEnabled) {
this.__$mobRenderEnd = Date.now()
}
})
if (exception) {
errorsReporter.emit(exception)
throw exception
}
return rendering
}
// ...
這樣就達(dá)到了用mobx控制react組件的render。
結(jié)論
對這一段代碼我們可以知道哥牍,僅僅在組件上加一行@observer然后使用mobx的可觀察屬性進(jìn)行render控制view毕泌,就能達(dá)到性能的高效化,對單個state屬性的粒度上控制組件的render嗅辣,所以放心大膽地用mobx來管理應(yīng)用的state吧撼泛!
下一篇 分批處理變化