離職了,把 2019 年在公司寫的文檔 copy 出來。年頭有點(diǎn)久,可能寫的不太對(duì)硅蹦,也不是很想改了~
注:本文檔對(duì)應(yīng) mobx 版本為 4.15.4、mobx-vue 版本為 2.0.10
對(duì)比 Vue 的猜想
observable 的變量對(duì)應(yīng) Vue 雙向綁定的 data 數(shù)據(jù)闷煤。Vue 實(shí)現(xiàn) data 的雙向綁定是在 initState 中提针,將 data 里面的每一個(gè)變量進(jìn)行遞歸的 defineProperty。所以猜測(cè) observable 也是一個(gè)遞歸建立 get(依賴收集) & set(派發(fā)更新) 的過程
源碼解析
- observable 函數(shù)入口
const observable: IObservableFactory & IObservableFactories & { enhancer: IEnhancer<any> } = createObservable as any
function createObservable(v: any, arg2?: any, arg3?: any) {
// @observable someProp;
if (typeof arguments[1] === "string") {
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
}
2.deepDecorator 裝飾器入口
這里只看裝飾器的用法
const deepDecorator = createDecoratorForEnhancer(deepEnhancer)
export function deepEnhancer(v, _, name) {
// it is an observable already, done
if (isObservable(v)) return v
// something that can be converted and mutated?
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
}
function createDecoratorForEnhancer(enhancer: IEnhancer<any>): IObservableDecorator {
invariant(enhancer)
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
defineObservableProperty(target, propertyName, initialValue, enhancer)
}
)
const res = ... // 就是返回一個(gè) decorator曹傀,只不過 NODE_ENV 不是生產(chǎn)環(huán)境會(huì)多打一個(gè)異常的 log
res.enhancer = enhancer
return res
}
createDecoratorForEnhancer 中的 createPropDecorator 是核心操作辐脖,也是 Mobx 裝飾器的通用核心邏輯,他的第二個(gè)入?yún)?propertyCreator皆愉,就是將來要用來生成 get & set 邏輯的重要回調(diào)嗜价,接下來看看 createPropDecorator 的源碼
- createPropDecorator
function createPropDecorator(
propertyInitiallyEnumerable: boolean,
propertyCreator: PropertyCreator
) {
return function decoratorFactory() {
let decoratorArguments: any[]
const decorator = function decorate(
target: DecoratorTarget,
prop: string,
descriptor: BabelDescriptor | undefined,
applyImmediately?: any
) {
if (applyImmediately === true) {
propertyCreator(target, prop, descriptor, target, decoratorArguments)
return null
}
if (process.env.NODE_ENV !== "production" && !quacksLikeADecorator(arguments))
fail("This function is a decorator, but it wasn't invoked like a decorator")
if (!Object.prototype.hasOwnProperty.call(target, mobxPendingDecorators)) {
const inheritedDecorators = target.__mobxDecorators
addHiddenProp(target, "__mobxDecorators", { ...inheritedDecorators })
}
target.__mobxDecorators![prop] = {
prop,
propertyCreator,
descriptor,
decoratorTarget: target,
decoratorArguments
}
return createPropertyInitializerDescriptor(prop, propertyInitiallyEnumerable)
}
if (quacksLikeADecorator(arguments)) {
// @decorator
decoratorArguments = EMPTY_ARRAY
return decorator.apply(null, arguments as any)
} else {
// @decorator(args)
decoratorArguments = Array.prototype.slice.call(arguments)
return decorator
}
} as Function
}
正常來說,到這一層幕庐,createPropertyInitializerDescriptor 需要 return 裝飾器所需的 descriptor久锥, 追下 createPropertyInitializerDescriptor 的源碼,你會(huì)發(fā)現(xiàn)返回的 descriptor 并不是我們想要的那種具有依賴收集派發(fā)更新邏輯的 get 和 set异剥,這只是一個(gè)初始化的鉤子而已
- createPropertyInitializerDescriptor
const enumerableDescriptorCache: { [prop: string]: PropertyDescriptor } = {}
const nonEnumerableDescriptorCache: { [prop: string]: PropertyDescriptor } = {}
function createPropertyInitializerDescriptor(
prop: string,
enumerable: boolean
): PropertyDescriptor {
const cache = enumerable ? enumerableDescriptorCache : nonEnumerableDescriptorCache
return ( // 這里確實(shí)就是 createObservable 的最終返回值
cache[prop] ||
(cache[prop] = {
configurable: true,
enumerable: enumerable,
get() {
initializeInstance(this)
return this[prop]
},
set(value) {
initializeInstance(this)
this[prop] = value
}
})
)
}
- initializeInstance
function initializeInstance(target: any)
function initializeInstance(target: DecoratorTarget) {
if (target.__mobxDidRunLazyInitializers === true) return
const decorators = target.__mobxDecorators
if (decorators) {
addHiddenProp(target, "__mobxDidRunLazyInitializers", true)
for (let key in decorators) {
const d = decorators[key]
d.propertyCreator(target, d.prop, d.descriptor, d.decoratorTarget, d.decoratorArguments)
}
}
}
這里的 decorators瑟由,也就是 target.__mobxDecorators, 是在 createPropDecorator 環(huán)節(jié)塞進(jìn)去的 propertyCreator 的相關(guān)變量冤寿,在這里進(jìn)行遍歷調(diào)用歹苦,最終會(huì)執(zhí)行每個(gè) decorators 的 defineObservableProperty(target, propertyName, initialValue, enhancer) 語句
- defineObservableProperty
其實(shí)是遍歷 decorators 時(shí) propertyCreator 內(nèi)的 defineObservableProperty 操作
function defineObservableProperty(
target: any,
propName: string,
newValue,
enhancer: IEnhancer<any>
) {
/**
* asObservableObject 的 作用是在 類上增加了 $mobx 字段
* 給 $mobx 字段賦值并 return 一個(gè) new ObservableObjectAdministration(target, name, defaultEnhancer)
*/
const adm = asObservableObject(target)
assertPropertyConfigurable(target, propName)
if (hasInterceptors(adm)) {
const change = interceptChange<IObjectWillChange>(adm, {
object: target,
name: propName,
type: "add",
newValue
})
if (!change) return
newValue = (change as any).newValue
}
// 然后在 ObservableObjectAdministration 的 value 上掛 ObservableValue
const observable = (adm.values[propName] = new ObservableValue(
newValue,
enhancer,
`${adm.name}.${propName}`,
false
))
newValue = (observable as any).value // observableValue might have changed it
// 核心邏輯
Object.defineProperty(target, propName, generateObservablePropConfig(propName))
if (adm.keys) adm.keys.push(propName)
notifyPropertyAddition(adm, target, propName, newValue)
}
- generateObservablePropConfig
function generateObservablePropConfig(propName) {
return (
observablePropertyConfigs[propName] ||
(observablePropertyConfigs[propName] = {
configurable: true,
enumerable: true,
get() {
return this.$mobx.read(this, propName)
},
set(v) {
this.$mobx.write(this, propName, v)
}
})
)
}
this.$mobx 就是上面的那個(gè) adm 即 new ObservableObjectAdministration,依賴收集發(fā)生在this.$mobx.read督怜,而派發(fā)更新則是在 this.$mobx.write殴瘦,Mobx 具體的依賴收集和派發(fā)更新的邏輯會(huì)在 derivation 的依賴收集過程 和 derivation 的派發(fā)更新過程 中講解
結(jié)論
其實(shí)繞了一圈,observable裝飾器大致就干了這么一件事情?
function observable(target:any, argument?:any): any {
return {
configurable: true,
enumerable: true,
get() {
...
},
set(v: any) {
... // 其他核心操作
Object.defineProperty(this, argument, {
configurable: true,
enumerable: true,
get() {
...
},
set(v: any) {
...
}
})
...
}
}
}
說下我個(gè)人的關(guān)注點(diǎn):
- enhancer 是之前說的 deepEnhancer 号杠,也就是對(duì)不同類型的各種 observe 的遞歸加工操作蚪腋,初始化的時(shí)候會(huì)觸發(fā),set 一個(gè)新 value 的時(shí)候也會(huì)觸發(fā)姨蟋,如果你 @observable 的變量是個(gè) string 或者 number屉凯,你會(huì)發(fā)現(xiàn) deepEnhancer 是覆蓋不到的,會(huì)直接 return 眼溶,observable 的函數(shù)式調(diào)用直接傳進(jìn)去一個(gè) number 也會(huì)報(bào)錯(cuò)悠砚,提示你使用 observable.box 來進(jìn)行對(duì)應(yīng)的操作。本來關(guān)注 deepEnhancer 是想看看裝飾器的代碼和函數(shù)的代碼是不是有比較高程度的復(fù)用偷仿,除了都在 deepEnhancer 以外哩簿,大流程基本是不太一樣的
- 最終 descriptor 里的 this.$mobx 指的是 ObservableObjectAdministration ,他的 get 是返回 value 上對(duì)應(yīng)響應(yīng)式變量的值酝静、而 set 則是改變這個(gè)值节榜,和一系列響應(yīng)。所有的變量都會(huì)在 ObservableObjectAdministration 一個(gè)名為 values 的 對(duì)象上:例如 value => ObservableValue别智;其中 ObservableValue: {key: value, value extends Atom}
- 通過 裝飾器 和 函數(shù) 創(chuàng)建的 observable 宗苍,雖然都是靠 this.$mobx 即 ObservableObjectAdministration 的 read 和 write 來進(jìn)行依賴收集和派發(fā)更新的,但是 @observable 的操作入口薄榛,掛在 ObservableObjectAdministration.value 上面讳窟, observable() 則是通過注入的 adm 也就是 ObservableObjectAdministration 實(shí)例, 來進(jìn)行相應(yīng)的操作。但是 @observable 如果是一個(gè)受 deepEnhancer 影響的引用類型敞恋,其成員都是走的 observable() 邏輯
- 因?yàn)轫?xiàng)目使用 mobx4丽啡,而 demo 使用了 mobx5,期間對(duì)比了 mobx4 和 mobx5 里 @observable 的源碼硬猫,主要的區(qū)別就是:
a. 在 4版本 里面 $mobx 就是 $mobx 的字符串补箍, 5版本則是個(gè) Symbol
b. this.$mobx 的 value 4 版本里是一個(gè)是對(duì)象, 5 版本是 Map
c. 上述源碼解析的第 6 步的執(zhí)行的操作不一樣啸蜜,5 對(duì)應(yīng)的操作是 asObservableObject(target).addObservableProp(propertyName, initialValue, enhancer)坑雅,但是原理是一樣的,都是 new 一個(gè) ObservableObjectAdministration衬横,然后往他的 value 上掛 ObservableValue裹粤,然后執(zhí)行 Object.defineProperty,其中 descriptor 參數(shù)是通過 generateObservablePropConfig 返回的