第一次閲讀源碼,可能有理解的不太正確的地方希望大佬們能幫我糾正。開始看的是6,後來看到observable發現和5的差距還是有一點的,所以在所以“autorun”的部分可能會有6的源碼,但差距並不大。
1.mobx的基本概念
Observable 被觀察者
Observer 觀察
Reaction 響應
var student = mobx.observable({
name: '張三',
});
mobx.autorun(() => {
console.log('張三的名字:', student.name);
});
2.mobx的原理
1.在響應式函數中(如以上autorun中通常會訪問一個或多個observable對象),
- 1)autorun首先創建一個Reaction類型的實例對象reaction,通過參數track一個響應式函數的回調函數。
- 2)然後執行reaction.schedule_方法,執行回調函數,回調函數中調用被觀察者observable.get方法,觸發reportObserved方法。
- 3)reportObserved方法中會將observavle對象收集到globalState.trackingDerivation.newObserving_隊列中(globalState.trackingDerivation此時等同於reaction對象)
- 4)處理reaction和observable的依賴關係,遍歷reaction.newObserving_屬性,在newObserving_隊列中的每一個observable.observers_屬性中添加當前reaction對象。
2.被觀察者observable的value發生變化,調用observable對象set方法,觸發propagateChange方法。propagateChange方法中,遍歷observable.observers_屬性依次執行reaction.onBecomeStale方法,再次將以上的2)3)4)執行一遍。
3.源碼解讀–autorun
以下為刪減後的代碼
3.1 autorun
export function autorun(
view: (r: IReactionPublic) => any,// autoruan函數的回調函數
opts: IAutorunOptions = EMPTY_OBJECT
): IReactionDisposer {
const name: string = "Autorun"
const runSync = !opts.scheduler && !opts.delay
// 首先創建一個Reaction類型的對象 主要功能是用來控制任務的執行
let reaction = new Reaction(
name,
function (this: Reaction) {
this.track(reactionRunner)
},
opts.onError,
opts.requiresObservable
)
function reactionRunner() { view(reaction) } // view即autorun函數的回調
reaction.schedule_() // 立即執行一次部署
return reaction.getDisposer_() // 用於在執行期間清理 autorun
}
從上邊的源碼可以看出autorun主要做了一下三個動作
- 1)創建一個Reaction類型的對象 主要功能是用來控制任務的執行
- 2)將view即auto的回調函數,分配給reaction.track
- 3)立即執行一次部署 ,此時你應該理解文檔中所説的“當使用autorun時,所提供的函數總是立即被觸發”
3.2 reaction.schedule_的源碼
schedule_() {
if (!this.isScheduled_) {
this.isScheduled_ = true
globalState.pendingReactions.push(this) // 當前的reaction對象入列
runReactions() // 隊列中的所有reaction對象執行runReaction_方法
}
}
function runReactionsHelper() {
let remainingReactions = allReactions.splice(0)
for (let i = 0, l = remainingReactions.length; i < l; i++)
remainingReactions[i].runReaction_()
}
schedule_方法做了兩件事
- 1)當前的reaction對象入列
- 2)隊列中的所有reaction對象執行runReaction_方法
3.3 runReaction_源碼
runReaction_() {
startBatch() // 開啓一層事務
this.isScheduled_ = false
if (shouldCompute(this)) {// derivation.dependenciesState_默認為-1 (未跟蹤)
this.onInvalidate_()
}
endBatch() // 關閉一層事務 startBatch和endBatch總是成對出現
}
翻看上邊的代碼可以發現onInvalidate_是在初始化Reaction時傳入構造函數的,實際上時調用了reaction.track方法
track(fn: () => void) {
startBatch()
...
const result = trackDerivedFunction(this, fn, undefined) // 執行任務 更新依賴
...
endBatch()
}
3.4 track方法主要是調用了trackDerivedFunction
export function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) {
...
globalState.trackingDerivation = derivation // 將derivation(此處等同於reaction對象)掛載到全局變量 這樣其他成員也可訪問此derivation
...
// 執行reaction傳遞的的回調方法,翻看代碼可以看出執行的是autoran函數的回調方法
// 回調中一般會調用一或多個observable對象,觸發observable.get方法,再觸發reportObserved方法
let result = f.call(context)
...
globalState.trackingDerivation = prevTracking
bindDependencies(derivation) // 更新observable和raction的依賴關係
return result
}
執行因為autorun回調用到了student.name變量,這裏的"."其實就是get操作;一旦設計到get操作,監督這個name的屬性的觀察員就會執行reportObserved方法(後邊介紹Oobservable時候會重點介紹這裏)。
3.5 reportObserved源碼
export function reportObserved(observable: IObservable): boolean {
...
const derivation = globalState.trackingDerivation
if (derivation !== null) {
if (derivation.runId_ !== observable.lastAccessedBy_) {
// 更被觀察者的lastAccessedBy_屬性(事務id),這個是為了避免重複操作
observable.lastAccessedBy_ = derivation.runId_
// 更新derivation(此處為reaction)的newObserving屬性,將被觀察者加入該隊列中
// 後續derivation和observable更新依賴關係就靠這個屬性
derivation.newObserving_![derivation.unboundDepsCount_++] = observable
...
}
return true
} else if (observable.observers_.size === 0 && globalState.inBatch > 0) {
queueForUnobservation(observable)
}
return false
}
上邊的代碼,我們主要關注影響derivation的操作
- 1)更新observable的lastAccessedBy_屬性(事務id),這個是為了避免重複操作。
- 2)更新derivation(此處為reaction)的newObserving屬性,將observable加入該隊列中,後續derivation和observable更新依賴關係就靠這個屬性
隨後autorun的任務執行完成後,derivation就開始着手更新和被觀察者observable的依賴關係
3.6 bindDependencies源碼
function bindDependencies(derivation: IDerivation) {
const prevObserving = derivation.observing_
// derivation.newObserving_為derivation依賴的observable對象的隊列
const observing = (derivation.observing_ = derivation.newObserving_!)
let lowestNewObservingDerivationState = IDerivationState_.UP_TO_DATE_ // 默認為0
let i0 = 0,
l = derivation.unboundDepsCount_
for (let i = 0; i < l; i++) {
/**
* 以下是一個去重的過程
* observable.diffValue_默認是0
* 循環時候置為1,因為observing為Observable類型的對象數組,所以不同位置上相同的值的diffValue_都會變成1
* 在遍歷到重複項後就不會進入下邊的判斷,i0就不會++
* 遍歷到非重複項(diffValue_為0的項),則直接將此項填充到i0對應的位置上
* 這樣數組循環完畢,i0即非重複項的數量,observing.length = i0即刪除掉了多餘項
*/
const dep = observing[i]
if (dep.diffValue_ === 0) {
dep.diffValue_ = 1
if (i0 !== i) observing[i0] = dep
i0++
}
}
observing.length = i0
derivation.newObserving_ = null // newObserving 置空
/**
* prevObserving中和observing中存在的均為observable對象
* 此時如果在上邊的循環完成後 observing存在的observable對象的diffValue_均為1
* 在prevObserving隊列如果是diffValue_仍然為0,表示當前derivation已經不依賴此observable對象
*/
l = prevObserving.length
while (l--) {
const dep = prevObserving[l];
if (dep.diffValue_ === 0) {
// 將當前derivation已不再依賴此observable對象,將其從observable.observers_中的deleted掉
removeObserver(dep, derivation)
}
dep.diffValue_ = 0 // 將prevObserving隊列中的observable的diffValue_均置為0
}
while (i0--) {
const dep = observing[i0]
// observing仍然為1的説明此observable對象不在prevObserving隊列中
if (dep.diffValue_ === 1) {
dep.diffValue_ = 0
// 在observable.observers_中添加當前的derivation對象
addObserver(dep, derivation)
}
}
// 通過以上的3次循環,將derivation.observing更新為最新的依賴(並去重),
// 並在已經不依賴的observable對象的observers_中delete當前的derivation對象
// 在新建立起的依賴的observable對象的observers_中add當前的derivation對象
}
響應被觀察者observable對象的value發生變化
上邊提及,一旦observable的value發生變化,就會觸發observable.get方法,然後觸發propagateChange方法,propageateChange源碼如下
export function propagateChanged(observable: IObservable) {
...
observable.observers_.forEach(d => {
...
d.onBecomeStale_()
...
})
}
observable.observers_存儲的是,與observable對象有依賴關係的derivation對象,在propagateChanged方法中,遍歷observers_執行derivation對象的onBecomeStale_方法,我們來看一下onBecomeStale_的源碼
3.7 onBecomeStale_的源碼
onBecomeStale_() {
this.schedule_()
}
this.schedule_是不是很熟悉,翻一下上邊的代碼,發現是在autorun函數中創建reaction對像的時候調用了reaction.schedule_()。所以這下明白propagateChanged調用onBecomeStale_是讓reaction再次執行一次之前的部署操作(也就是執行autorun的回調,處理依賴關係);
4.接下來開始看observable(被觀察者)部分
4.1 observable的別名createObservable
export const observable: IObservableFactory &
IObservableFactories & {
enhancer: IEnhancer<any>
} = createObservable as any // observable的別名createObservable
// 將observableFactories的屬性複製一份給observable
Object.keys(observableFactories).forEach(name => (observable[name] = observableFactories[name]))
- 1)首先 observable 是函數函數同 createObservable。
- 2)observable複製了observableFactories的屬性。
function createObservable(v: any, arg2?: any, arg3?: any) {
// @observable someProp;
if (typeof arguments[1] === "string" || typeof arguments[1] === "symbol") {
return deepDecorator.apply(null, arguments as any)
}
// it is an observable already, done
if (isObservable(v)) return v
// something that can be converted and mutated?
const res = isPlainObject(v)
? observable.object(v, arg2, arg3)
: Array.isArray(v)
? observable.array(v, arg2)
: isES6Map(v)
? observable.map(v, arg2)
: isES6Set(v)
? observable.set(v, arg2)
: v
// this value could be converted to a new observable data structure, return it
if (res !== v) return res
}
createObservable方法起到了轉發的作用,將傳入的對象轉發給具體的轉換函數。
簡單分析一下具體的轉化模式
- 1)arguments[1] === “string” || typeof arguments[1] === “symbol” 採用的是裝飾器@observable,裝飾器的參數(target,prop,descriptor)其中arguments[1] 也就是prop為屬性名稱為字符串類型
- 2)isObservable(v) 已經轉換為觀察值了不需要再轉換
- 3)observable.object、observable.array、observable.map、observable.set根據傳入參數的類型分別調用具體的轉換方法
- 4)針對原始類型提示用户建議使用observable.box方法
4.2 observable.box
observable.box在文檔中是這樣介紹的。
observable.box把普通的值轉換成可觀察的值,如下例。
const name = observable.box("張三");
console.log(name.get());
// 輸出 '張三'
name.observe(function(change) {
console.log(change.oldValue, "->", change.newValue);
});
name.set("李四");
// 輸出 '張三 -> 李四'
observable.box retrun 一個ObservableValue類型的對像。
box<T = any>(value?: T, options?: CreateObservableOptions): IObservableValue<T> {
const o = asCreateObservableOptions(options) // 格式化入參
// ObservableValue的擁有方法get set observe intercept...
return new ObservableValue(value, getEnhancerFromOptions(o), o.name, true, o.equals)
},
案例中的“name.set(“李四”)”,就是調用了ObservableValue的set方法。一會再介紹ObservableValue的時候會重點説下。
4.3 核心類 ObservableValue
ObservableValue 繼承了 Atom原子類,先梳理一下Atom和ObservableValue和有什麼主要能力。
Atom
public reportObserved(): boolean {
return reportObserved(this)
}
public reportChanged() {
startBatch()
propagateChanged(this)
endBatch()
}
ObservableValue
public set(newValue: T) {
const oldValue = this.value
newValue = this.prepareNewValue(newValue) as any
if (newValue !== globalState.UNCHANGED) {
const oldValue = this.value
this.value = newValue
this.reportChanged()
...
}
}
public get(): T {
this.reportObserved()
return this.dehanceValue(this.value)
}
intercept
observe
其中reportObserved、propagateChanged在梳理autorun的時候介紹過。
- 1)reportObserved:調用觀察值是用於更新derivation和observable的依賴關係。
- 2)propagateChanged:觀察值改變時,observable對象的observers中存儲的derivation,執行onBecomeStale方法,重新執行部署操作。
- 3)Observablevalue的set 修改value同時調用Atom的reportChanged方法觸發propagateChanged。
- 4)Observablevalue的get 獲取value值的同時調用Atom的reportObserved方法觸發reportObserved。
所以上邊案例中“name.set(“李四”);”會觸發propagateChanged方法,會執行有依賴關係的 derivation 重新執行部署操作
接下來看一下new ObservableValue的時候幹了什麼?
constructor(
value: T,
public enhancer: IEnhancer<T>,
public name = "ObservableValue@" + getNextId(),
notifySpy = true,
private equals: IEqualsComparer<any> = comparer.default
) {
...
this.value = enhancer(value, undefined, name)
}
ObservableValue的構造函數中調用enhancer對value進行了處理,enhancer是通過參數是創建ObservableValue類型對象是傳遞的參數getEnhancerFromOptions(o)。getEnhancerFromOptions默認返回的是deepEnhancer。
function getEnhancerFromOptions(options: CreateObservableOptions): IEnhancer<any> {
return options.defaultDecorator
? options.defaultDecorator.enhancer
: options.deep === false
? referenceEnhancer
: deepEnhancer
}
gdeepEnhancer主要內容如下。
export function deepEnhancer(v, _, name) {
if (isObservable(v)) return v
if (Array.isArray(v)) return observable.array(v, { name })
if (isPlainObject(v)) return observable.object(v, undefined, { name })
if (isES6Map(v)) return observable.map(v, { name })
if (isES6Set(v)) return observable.set(v, { name })
return v
}
這個deepEnhancer是不是看上去有點眼熟,往上翻一下可以看出他和createObservable 函數十分相似,起到了轉發的作用,將傳入的對象轉發給具體的轉換函數。所以要理解observable我門主要就是要了解這些轉換函數。接下來我們主要分析observable.object。
4.4 observable.object
object<T = any>(
props: T,
decorators?: { [K in keyof T]: Function },
options?: CreateObservableOptions
): T & IObservableObject {
const o = asCreateObservableOptions(options)
if (o.proxy === false) {
return extendObservable({}, props, decorators, o) as any
} else {
const defaultDecorator = getDefaultDecoratorFromObjectOptions(o)
const base = extendObservable({}, undefined, undefined, o) as any
const proxy = createDynamicObservableObject(base)
extendObservableObjectWithProperties(proxy, props, decorators, defaultDecorator)
return proxy
}
}
o.proxy為true的時候只是多了一步Proxy,其餘的工作基本相似,所以主要關注extendObservable方法就可以了。
extendObservable中調主要用了getDefaultDecoratorFromObjectOptions、asObservableObject、extendObservableObjectWithProperties方法。因為getDefaultDecoratorFromObjectOptions與extendObservableObjectWithProperties有關聯,所以先來看asObservableObject,再看另外兩個方法。
4.5 extendObservable
export function extendObservable<A extends Object, B extends Object>(
target: A,
properties?: B,
decorators?: { [K in keyof B]?: Function },
options?: CreateObservableOptions
): A & B {
options = asCreateObservableOptions(options)
const defaultDecorator = getDefaultDecoratorFromObjectOptions(options) // 默認返回deepDecorator裝飾器
asObservableObject(target, options.name, defaultDecorator.enhancer) // make sure object is observable, even without initial props
if (properties)
extendObservableObjectWithProperties(target, properties, decorators, defaultDecorator)
return target as any
}
4.6 asObservableObject
asObservableObject方法:
- 1)創建一個對象amd為ObservableObjectAdministration類的實例。
- 1)amd賦值給target[$mobx]
- 2)返回amd;
export function asObservableObject(
target: any,
name: PropertyKey = "",
defaultEnhancer: IEnhancer<any> = deepEnhancer
): ObservableObjectAdministration {
const adm = new ObservableObjectAdministration(
target,
new Map(),
stringifyKey(name),
defaultEnhancer
)
addHiddenProp(target, $mobx, adm)
return adm
}
4.7 extendObservableObjectWithProperties
extendObservableObjectWithProperties:循環原始對象,對每一個屬性值經過都decorator函數處理(decorators方法即通過getDefaultDecoratorFromObjectOptions方法獲取的默認為deepDecorator,所以一回直接看deepDecorator)
export function extendObservableObjectWithProperties(
target,
properties, // 原對象
decorators,
defaultDecorator
) {
startBatch()
const keys = ownKeys(properties)
// 循環原對象
for (const key of keys) {
const descriptor = Object.getOwnPropertyDescriptor(properties, key)!
const decorator =
decorators && key in decorators
? decorators[key]
: descriptor.get
? computedDecorator
: defaultDecorator
const resultDescriptor = decorator!(target, key, descriptor, true) // 經過裝飾器處理
if (
resultDescriptor // otherwise, assume already applied, due to `applyToInstance`
)
Object.defineProperty(target, key, resultDescriptor)
}
endBatch()
}
4.8 decorator
decorator默認為deepDecorator,我們來看一下它都幹了什麼。
export function createDecoratorForEnhancer(enhancer: IEnhancer<any>): IObservableDecorator {
const decorator = createPropDecorator(
true,
(
target: any,
propertyName: PropertyKey,
descriptor: BabelDescriptor | undefined,
_decoratorTarget,
decoratorArgs: any[]
) => {
const initialValue = descriptor
? descriptor.initializer
? descriptor.initializer.call(target)
: descriptor.value
: undefined
// 調用target[$mobx].addObservableProp方法
asObservableObject(target).addObservableProp(propertyName, initialValue, enhancer)
}
)
const res: any = decorator
res.enhancer = enhancer
return res
}
4.9 addObservableProp方法
decorator中調用了target[$mobx].addObservableProp方法
addObservableProp(
propName: PropertyKey,
newValue,
enhancer: IEnhancer<any> = this.defaultEnhancer
) {
const { target } = this
if (hasInterceptors(this)) {
// 攔截處理
const change = interceptChange<IObjectWillChange>(this, {
object: this.proxy || target,
name: propName,
type: "add",
newValue
})
if (!change) return // 攔截器返回空的時候不需要重新忽略此次修改。
newValue = (change as any).newValue
}
// newValue轉換成ObservableValue類型
const observable = new ObservableValue(
newValue,
enhancer,
`${this.name}.${stringifyKey(propName)}`,
false
)
this.values.set(propName, observable) // 存儲
newValue = (observable as any).value
// generateObservablePropConfig方法返回以下描述符
// { ..., get() { return this[$mobx].read(propName) }, set(v) { this[$mobx].write(propName, v) } }
Object.defineProperty(target, propName, generateObservablePropConfig(propName)) // target生成propName屬性
const notify = hasListeners(this)
const change = {
type: "add",
object: this.proxy || this.target,
name: propName,
newValue
}
this.keysAtom.reportChanged() // this.keysAtom即Atom的實例
}
addObservableProp方法
- 1)調用ObservableValue類將newValue轉換為可觀察值(還記不記得上邊ObservableValue調用通過enhancer調用了observable.object方法嗎。現在可以看出observable.object方法中在循環對象的屬性時又調用了ObservableValue。通過這種遞歸的方式將對象的屬性轉換為可觀察值)
- 2)將屬性key和observable存入target[$mobx].values中
- 3)將原對象屬性值添加到target,並通過描述符中get和set都是直接調用this[mobx].read和this[mobx].write方法。
- 4)調用原子類Atom的reportChanged,讓依賴此observable對象的derivation重新執行部署操作。
綜上extendObservableObjectWithProperties作用即循環原始對象,執行以上4步,實現了將原始對象的屬性代理到target上,並將值轉換到可觀察值,存儲在target[$mobx].values中。
4.10 read和write
read(key: PropertyKey) {
return this.values.get(key)!.get()
}
// observable.get方法
public get(): T {
this.reportObserved() // Atom下的reportObserved
return this.dehanceValue(this.value)
}
read方法會根據屬性名稱從this.values中查找,獲取到對應的observable對象再調用observable.get方法觸發reportObserved
write(key: PropertyKey, newValue) {
const instance = this.target
const observable = this.values.get(key)
// intercept
if (hasInterceptors(this)) {
const change = interceptChange<IObjectWillChange>(this, {
type: "update",
object: this.proxy || instance,
name: key,
newValue
})
if (!change) return
newValue = (change as any).newValue
}
newValue = (observable as any).prepareNewValue(newValue)
if (newValue !== globalState.UNCHANGED) {
(observable as ObservableValue<any>).setNewValue(newValue)
}
}
// observable.prepareNewValue和observable.setNewValue方法
private prepareNewValue(newValue): T | IUNCHANGED {
if (hasInterceptors(this)) {
const change = interceptChange<IValueWillChange<T>>(this, {
object: this,
type: "update",
newValue
})
if (!change) return globalState.UNCHANGED
newValue = change.newValue
}
// apply modifier
newValue = this.enhancer(newValue, this.value, this.name) // 調用enhancer轉換為可觀察模式
return this.equals(this.value, newValue) ? globalState.UNCHANGED : newValue
}
setNewValue(newValue: T) {
const oldValue = this.value
this.value = newValue
this.reportChanged()
if (hasListeners(this)) {
notifyListeners(this, {
type: "update",
object: this,
newValue,
oldValue
})
}
}
write方法
- 1)調用observable.prepareNewValue方法將新的value進行轉換
- 2)調用observable.setNewValue重新修改值
- 3)觸發reportChanged方法。
4.11 綜上總結
var student = mobx.observable({
name: '張三',
});
mobx通過observable方法用target代理了傳入的對象,賦值給student。
因此student的結構應該如下
在調用student.name時候觸發會調用get=>read=>observableValue.get=>reportObserved
修改的時候 set=>write=>observableValue.setNewValue=>reportChanged
現在基本可以理解observable是autoruan之間的關係了。