Vue源碼深入生命周期

  • 當我們調用new Vue的時候,事實上就調用的Vue原型上的_init方法.
    initLifecycle(vm)  
// lifecycle初始化生命周期

    initEvents(vm)     
// events初始化 vm._events, 主要是提供vm實例上的$on/$emit/$off/$off等方法

    initRender(vm)     
// 初始化渲染函數,在vm上綁定$createElement方法

    callHook(vm, 'beforeCreate')  
// 鉤子函數的執行, beforeCreate

    initInjections(vm) 
// resolve injections before data/props

    initState(vm)      
// Observe data添加對data的監聽, 將data轉化為getters/setters

    initProvide(vm) 
// resolve provide after data/props

    callHook(vm, 'created') 
// 鉤子函數的執行, created
  • this._init()方法中調用initState(vm),完成對vm這個實例的數據的監聽
  // 初始化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()方法中調用initProps (vm: Component, propsOptions: Object)去將props傳入,函數中的這個validateProp方法,不僅完成了prop屬性類型驗證的,同時將prop的值都轉化為了getter/setter,并返回一個observer
  for (const key in propsOptions) {
    // this._init傳入的options中的props屬性
    keys.push(key)

    // 注意這個validateProp方法,不僅完成了prop屬性類型驗證的,同時將prop的值都轉化為了getter/setter,并返回一個observer
    const value = validateProp(key, propsOptions, propsData, vm)
   
    // 將這個key對應的值轉化為getter/setter
      defineReactive(props, key, value)

    // 如果在vm這個實例上沒有key屬性,那么就通過proxy轉化為proxyGetter/proxySetter, 并掛載到vm實例上,可以通過app._props[key]這種形式去訪問
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  observerState.shouldConvert = true

// 獲取prop的默認值
function getPropDefaultValue

  // 如果沒有default屬性的話,那么就返回undefined
  if (!hasOwn(prop, 'default')) {
    return undefined
  }

Vue提供了一個observe方法,在其內部實例化了一個Observer類,并返回Observer的實例。每一個Observer實例對應記錄了props中這個的default value的所有依賴(僅限object類型),這個Observer實際上就是一個觀察者,它維護了一個數組this.subs = []用以收集相關的subs(訂閱者)(即這個觀察者的依賴)。通過將default value轉化為getter/setter形式,同時添加一個自定義ob屬性,這個屬性就對應Observer實例。

在往上數的第二段代碼里面的方法obervse(value),即對{key1: 'a', key2: {a: 'b'}}進行依賴的管理,同時將這個obj所有的屬性值都轉化為getter/setter形式。此外,Vue還會將props屬性都代理到vm實例上,通過vm.a就可以訪問到這個屬性。

  • 那么Vue是如何來實現訂閱者的呢?Vue里面定義了一個類: Watcher,在Vue的整個生命周期當中,會有4類地方會實例化Watcher:

Vue實例化的過程中有watch選項
Vue實例化的過程中有computed計算屬性選項
Vue原型上有掛載$watch方法: Vue.prototype.$watch,可以直接通過實例調用this.$watch方法
Vue生成了render函數,更新視圖時

Watcher接收的參數當中expOrFn定義了用以獲取watcher的getter函數。expOrFn可以有2種類型:string或function.若為string類型,首先會通過parsePath方法去對string進行分割(僅支持.號形式的對象訪問)。在除了computed選項外,其他幾種實例化watcher的方式都是在實例化過程中完成求值及依賴的收集工作:this.value = this.lazy ? undefined : this.get().在Watcher的get方法中:

一進入get方法,首先進行pushTarget(this)的操作,此時Vue當中Dep.target = 當前這個watcher,接下來進行value = this.getter.call(vm, vm)操作,在這個操作中就完成了依賴的收集工作。還是拿文章一開始的demo來說,在vue實例化的時候傳入了watch選項:

在Vue的initState()開始執行后,首先會初始化props的屬性為getter/setter函數,然后在進行initWatch初始化的時候,這個時候初始化watcher實例,并調用get()方法,設置Dep.target = 當前這個watcher實例,進而到value = this.getter.call(vm, vm)的操作。在調用this.getter.call(vm, vm)的方法中,便會訪問props選項中的a屬性即其getter函數。在a屬性的getter函數執行過程中,因為Dep.target已經存在,那么就進入了依賴收集的過程:

dep是一開始初始化的過程中,這個屬性上的dep屬性。調用dep.depend()函數:

Dep.target也就剛才的那個watcher實例,這里也就相當于調用了watcher實例的addDep方法: watcher.addDep(this),并將dep觀察者傳入。在addDep方法中完成依賴收集:

這個時候依賴完成了收集,當你去修改a屬性的值時,會調用a屬性的setter函數,里面會執行dep.notify(),它會遍歷所有的訂閱者,然后調用訂閱者上的update函數。

initData過程和initProps類似,具體可參見源碼。

  • initComputed

以上就是在initProps過程中Vue是如何進行依賴收集的,initData的過程和initProps類似,下來再來看看initComputed的過程.
在computed屬性初始化的過程當中,會為每個屬性實例化一個watcher:

但是這個watcher在實例化的過程中,由于傳入了{lazy: true}的配置選項,那么一開始是不會進行求值與依賴收集的: this.value = this.lazy ? undefined : this.get().在initComputed的過程中,Vue會將computed屬性定義到vm實例上,同時將這個屬性定義為getter/setter。當你訪問computed屬性的時候調用getter函數:

在watcher存在的情況下,首先判斷watcher.dirty屬性,這個屬性主要是用于判斷這個computed屬性是否需要重新求值,因為在上一輪的依賴收集的過程當中,觀察者已經將這個watcher添加到依賴數組當中了,如果觀察者發生了變化,就會dep.notify(),通知所有的watcher,而對于computed的watcher接收到變化的請求后,會將watcher.dirty = true即表明觀察者發生了變化,當再次調用computed屬性的getter函數的時候便會重新計算,否則還是使用之前緩存的值。

  • initWatch

initWatch的過程中其實就是實例化new Watcher完成觀察者的依賴收集的過程,在內部的實現當中是調用了原型上的Vue.prototype.$watch方法。這個方法也適用于vm實例,即在vm實例內部調用this.$watch方法去實例化watcher,完成依賴的收集,同時監聽expOrFn的變化。

總結:

以上就是在Vue實例初始化的過程中實現依賴管理的分析。大致的總結下就是:

initState的過程中,將props,computed,data等屬性通過Object.defineProperty來改造其getter/setter屬性,并為每一個響應式屬性實例化一個observer觀察者。這個observer內部dep記錄了這個響應式屬性的所有依賴。
當響應式屬性調用setter函數時,通過dep.notify()方法去遍歷所有的依賴,調用watcher.update()去完成數據的動態響應。
這篇文章主要從初始化的數據層面上分析了Vue是如何管理依賴來到達數據的動態響應。下一篇文章來分析下Vue中模板中的指令和響應式數據是如何關聯來實現由數據驅動視圖,以及數據是如何響應視圖變化的。

文章參考: Vue 2.0 的數據依賴實現原理簡析

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容