- 當(dāng)我們調(diào)用new Vue的時(shí)候,事實(shí)上就調(diào)用的Vue原型上的_init方法.
initLifecycle(vm)
// lifecycle初始化生命周期
initEvents(vm)
// events初始化 vm._events, 主要是提供vm實(shí)例上的$on/$emit/$off/$off等方法
initRender(vm)
// 初始化渲染函數(shù),在vm上綁定$createElement方法
callHook(vm, 'beforeCreate')
// 鉤子函數(shù)的執(zhí)行, beforeCreate
initInjections(vm)
// resolve injections before data/props
initState(vm)
// Observe data添加對(duì)data的監(jiān)聽, 將data轉(zhuǎn)化為getters/setters
initProvide(vm)
// resolve provide after data/props
callHook(vm, 'created')
// 鉤子函數(shù)的執(zhí)行, created
- this._init()方法中調(diào)用
initState(vm),完成對(duì)vm這個(gè)實(shí)例的數(shù)據(jù)的監(jiān)聽
// 初始化props屬性
if (opts.props) initProps(vm, opts.props)
// 初始化methods屬性
if (opts.methods) initMethods(vm, opts.methods)
// 初始化data屬性
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
// 初始化computed屬性
if (opts.computed) initComputed(vm, opts.computed)
// 初始化watch屬性
if (opts.watch) initWatch(vm, opts.watch)
- this._init()方法中調(diào)用
initProps (vm: Component, propsOptions: Object)去將props傳入,函數(shù)中的這個(gè)validateProp方法,不僅完成了prop屬性類型驗(yàn)證的,同時(shí)將prop的值都轉(zhuǎn)化為了getter/setter,并返回一個(gè)observer
for (const key in propsOptions) {
// this._init傳入的options中的props屬性
keys.push(key)
// 注意這個(gè)validateProp方法,不僅完成了prop屬性類型驗(yàn)證的,同時(shí)將prop的值都轉(zhuǎn)化為了getter/setter,并返回一個(gè)observer
const value = validateProp(key, propsOptions, propsData, vm)
// 將這個(gè)key對(duì)應(yīng)的值轉(zhuǎn)化為getter/setter
defineReactive(props, key, value)
// 如果在vm這個(gè)實(shí)例上沒有key屬性,那么就通過proxy轉(zhuǎn)化為proxyGetter/proxySetter, 并掛載到vm實(shí)例上,可以通過app._props[key]這種形式去訪問
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
observerState.shouldConvert = true
// 獲取prop的默認(rèn)值
function getPropDefaultValue
// 如果沒有default屬性的話,那么就返回undefined
if (!hasOwn(prop, 'default')) {
return undefined
}
Vue提供了一個(gè)observe方法,在其內(nèi)部實(shí)例化了一個(gè)Observer類,并返回Observer的實(shí)例。每一個(gè)Observer實(shí)例對(duì)應(yīng)記錄了props中這個(gè)的default value的所有依賴(僅限object類型),這個(gè)Observer實(shí)際上就是一個(gè)觀察者,它維護(hù)了一個(gè)數(shù)組this.subs = []用以收集相關(guān)的subs(訂閱者)(即這個(gè)觀察者的依賴)。通過將default value轉(zhuǎn)化為getter/setter形式,同時(shí)添加一個(gè)自定義ob屬性,這個(gè)屬性就對(duì)應(yīng)Observer實(shí)例。
在往上數(shù)的第二段代碼里面的方法obervse(value),即對(duì){key1: 'a', key2: {a: 'b'}}進(jìn)行依賴的管理,同時(shí)將這個(gè)obj所有的屬性值都轉(zhuǎn)化為getter/setter形式。此外,Vue還會(huì)將props屬性都代理到vm實(shí)例上,通過vm.a就可以訪問到這個(gè)屬性。
- 那么Vue是如何來實(shí)現(xiàn)訂閱者的呢?Vue里面定義了一個(gè)類: Watcher,在Vue的整個(gè)生命周期當(dāng)中,會(huì)有4類地方會(huì)實(shí)例化Watcher:
Vue實(shí)例化的過程中有watch選項(xiàng)
Vue實(shí)例化的過程中有computed計(jì)算屬性選項(xiàng)
Vue原型上有掛載$watch方法: Vue.prototype.$watch,可以直接通過實(shí)例調(diào)用this.$watch方法
Vue生成了render函數(shù),更新視圖時(shí)
Watcher接收的參數(shù)當(dāng)中expOrFn定義了用以獲取watcher的getter函數(shù)。expOrFn可以有2種類型:string或function.若為string類型,首先會(huì)通過parsePath方法去對(duì)string進(jìn)行分割(僅支持.號(hào)形式的對(duì)象訪問)。在除了computed選項(xiàng)外,其他幾種實(shí)例化watcher的方式都是在實(shí)例化過程中完成求值及依賴的收集工作:this.value = this.lazy ? undefined : this.get().在Watcher的get方法中:
一進(jìn)入get方法,首先進(jìn)行pushTarget(this)的操作,此時(shí)Vue當(dāng)中Dep.target = 當(dāng)前這個(gè)watcher,接下來進(jìn)行value = this.getter.call(vm, vm)操作,在這個(gè)操作中就完成了依賴的收集工作。還是拿文章一開始的demo來說,在vue實(shí)例化的時(shí)候傳入了watch選項(xiàng):
在Vue的initState()開始執(zhí)行后,首先會(huì)初始化props的屬性為getter/setter函數(shù),然后在進(jìn)行initWatch初始化的時(shí)候,這個(gè)時(shí)候初始化watcher實(shí)例,并調(diào)用get()方法,設(shè)置Dep.target = 當(dāng)前這個(gè)watcher實(shí)例,進(jìn)而到value = this.getter.call(vm, vm)的操作。在調(diào)用this.getter.call(vm, vm)的方法中,便會(huì)訪問props選項(xiàng)中的a屬性即其getter函數(shù)。在a屬性的getter函數(shù)執(zhí)行過程中,因?yàn)镈ep.target已經(jīng)存在,那么就進(jìn)入了依賴收集的過程:
dep是一開始初始化的過程中,這個(gè)屬性上的dep屬性。調(diào)用dep.depend()函數(shù):
Dep.target也就剛才的那個(gè)watcher實(shí)例,這里也就相當(dāng)于調(diào)用了watcher實(shí)例的addDep方法: watcher.addDep(this),并將dep觀察者傳入。在addDep方法中完成依賴收集:
這個(gè)時(shí)候依賴完成了收集,當(dāng)你去修改a屬性的值時(shí),會(huì)調(diào)用a屬性的setter函數(shù),里面會(huì)執(zhí)行dep.notify(),它會(huì)遍歷所有的訂閱者,然后調(diào)用訂閱者上的update函數(shù)。
initData過程和initProps類似,具體可參見源碼。
- initComputed
以上就是在initProps過程中Vue是如何進(jìn)行依賴收集的,initData的過程和initProps類似,下來再來看看initComputed的過程.
在computed屬性初始化的過程當(dāng)中,會(huì)為每個(gè)屬性實(shí)例化一個(gè)watcher:
但是這個(gè)watcher在實(shí)例化的過程中,由于傳入了{(lán)lazy: true}的配置選項(xiàng),那么一開始是不會(huì)進(jìn)行求值與依賴收集的: this.value = this.lazy ? undefined : this.get().在initComputed的過程中,Vue會(huì)將computed屬性定義到vm實(shí)例上,同時(shí)將這個(gè)屬性定義為getter/setter。當(dāng)你訪問computed屬性的時(shí)候調(diào)用getter函數(shù):
在watcher存在的情況下,首先判斷watcher.dirty屬性,這個(gè)屬性主要是用于判斷這個(gè)computed屬性是否需要重新求值,因?yàn)樵谏弦惠喌囊蕾囀占倪^程當(dāng)中,觀察者已經(jīng)將這個(gè)watcher添加到依賴數(shù)組當(dāng)中了,如果觀察者發(fā)生了變化,就會(huì)dep.notify(),通知所有的watcher,而對(duì)于computed的watcher接收到變化的請(qǐng)求后,會(huì)將watcher.dirty = true即表明觀察者發(fā)生了變化,當(dāng)再次調(diào)用computed屬性的getter函數(shù)的時(shí)候便會(huì)重新計(jì)算,否則還是使用之前緩存的值。
- initWatch
initWatch的過程中其實(shí)就是實(shí)例化new Watcher完成觀察者的依賴收集的過程,在內(nèi)部的實(shí)現(xiàn)當(dāng)中是調(diào)用了原型上的Vue.prototype.$watch方法。這個(gè)方法也適用于vm實(shí)例,即在vm實(shí)例內(nèi)部調(diào)用this.$watch方法去實(shí)例化watcher,完成依賴的收集,同時(shí)監(jiān)聽expOrFn的變化。
總結(jié):
以上就是在Vue實(shí)例初始化的過程中實(shí)現(xiàn)依賴管理的分析。大致的總結(jié)下就是:
initState的過程中,將props,computed,data等屬性通過Object.defineProperty來改造其getter/setter屬性,并為每一個(gè)響應(yīng)式屬性實(shí)例化一個(gè)observer觀察者。這個(gè)observer內(nèi)部dep記錄了這個(gè)響應(yīng)式屬性的所有依賴。
當(dāng)響應(yīng)式屬性調(diào)用setter函數(shù)時(shí),通過dep.notify()方法去遍歷所有的依賴,調(diào)用watcher.update()去完成數(shù)據(jù)的動(dòng)態(tài)響應(yīng)。
這篇文章主要從初始化的數(shù)據(jù)層面上分析了Vue是如何管理依賴來到達(dá)數(shù)據(jù)的動(dòng)態(tài)響應(yīng)。下一篇文章來分析下Vue中模板中的指令和響應(yīng)式數(shù)據(jù)是如何關(guān)聯(lián)來實(shí)現(xiàn)由數(shù)據(jù)驅(qū)動(dòng)視圖,以及數(shù)據(jù)是如何響應(yīng)視圖變化的。