vue數據入口initSate開始分析數據驅動更新原理

前言

讀這篇文章前,最好是先讀vue數據綁定源碼,由於本篇是接這章寫的,放在一篇文章裏,篇幅太大,我只好分紅兩章了。vue

初始化vue實例

Vue.prototype._init = function (options) {
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm) // 初始化數據的入口
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')
    
    if (vm.$options.el) {
      vm.$mount(vm.$options.el) // 掛載組件
    }
}

// mountComponent是在掛載組件時調用的方法
export function mountComponent (vm, el ,hydrating) {
  callHook(vm, 'beforeMount')

  let updateComponent = () => {
      vm._update(vm._render(), hydrating)
  }

  new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */)
  
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  
  return vm
}

initState

上一篇文章,咱們已經瞭解了ObserverDepWatcher都是負責什麼?如何互相協做?
接下來,咱們從數據的入口開始,瞭解vue是如何使用這幾個類完成的數據驅動視圖更新的?
初始化state,就是初始化這幾個屬性。node

clipboard.png

先講下什麼是可觀察者對象呢?
具有兩個條件:
一、取值的時候,能把要取值的watcher(觀察者對象)加入它的dep(依賴,也可叫觀察者管理器)管理的subs列表裏(即觀察者列表);
二、設置值的時候,有了變化,全部依賴於它的對象(即它的dep裏收集到的觀察者watcher)都獲得通知。
watcher裏面存儲它都觀察了誰(dep),dep裏面存儲了都誰觀察了本身。segmentfault

initProps

clipboard.png

循環每一個props屬性,對每一個屬性調用defineReactive,把每一個屬性加上getset裝飾器,變爲可觀察者對象,若是屬性值是對象也會遞歸轉化。數組

initData

clipboard.png

observe就是循環把data中的全部項都轉換成可觀察者對象,若是子項是對象或數組就遞歸轉化,確保data裏的每一項及其後代都轉化成了可觀察者對象
初始化數據時,無論data裏的數據渲染用沒用到,都會轉成可觀察者對象。
對於propsdata,只有哪一個watcher用到了去讀取時,纔會把該watcher加到他的觀察者列表中。ide

dataprops裏都調用了proxy這個方法,他是作什麼的呢?
proxy(vm,’_data’,key)
proxy就是把dataprops下的屬性都代理到了vm實例下,vm._data.a等價於vm.a
原理就是Object.definePropertyvmkey屬性設置getset方法,當訪問get的時候,返回的是vm._data[key];當訪問set的時候,設置的是vm._data[key]的值。oop

initComputed

clipboard.png

初始化計算屬性,就是爲每個計算屬性定義一個Watcher觀察者對象。這個對象是lazy的,不會當即就去執行計算(即get方法),等到用的時候纔會去計算,這個時候就會去讀取這個計算屬性依賴的可觀察屬性的值來計算,讀取的時候就會把這些依賴添加進這個計算watcher裏,同時這些依賴的訂閱者列表也會加入這個計算watcher。因此當依賴變化時,通知到他的全部訂閱watcher。計算watcher接到依賴發生變化了,不會當即計算新值,而是標記dirtytrue,讀取這個計算屬性的時候,發現dirtytrue,就是說數據已經不是最新的了,須要從新計算,而後纔去計算,不然直接取上一次計算的值valuespa

若是某個data經過計算屬性間接的被用到了渲染裏,那麼這個data也會被加入到渲染watcher的依賴列表,它的訂閱者列表也會保存渲染watcher
只有當模版裏使用了該計算屬性,這個計算屬性依賴的可觀察者纔會被加入到渲染watcher的依賴列表。若是模版裏沒有直接或間接用到可觀察者對象屬性,那麼當你set它的時候,也就不會觸發更新操做。prototype

initWatch

clipboard.png

初始化watch,就是爲每一個watch屬性建立一個觀察者對象,這個expOrFn解析取值表達式去取值,而後就會調用相關data/prop屬性的get方法,get方法又會在他的觀察者列表里加上該watcher,一旦這些依賴屬性值變化就會通知該watcher執行update方法。即會執行他的回調方法cb,也就是watch屬性的handler方法。代理

到這裏,數據的初始化就完成了。code

mountComponent

clipboard.png

這個方法是在,全部的數據初始化完成後,執行掛載組件時調用,建立一個渲染watcher,每一個組件有且僅有一個渲染watcherupdateComponentwatchergetter屬性,建立後,當即調用get方法,便是調用updateComponent,也就是調用render方法,render裏使用了的數據就會讀取相關的dataget方法,就會把data的依賴加進這個渲染watcher的依賴列表裏,datasubs也會加入渲染watcher,如此,當設置data時攔截的set方法就會通知渲染watcher調用update方法。

我總結下:
一個數據變動後,通知他的Watcher去執行update。
不一樣類型的Watcher職責不一樣,vue裏的Watcher能夠分爲3類:
渲染Watcher、計算Watcher、偵聽器Watcher,注意這3中Watcher的getter屬性分別是什麼。

  • 渲染Watcher的update,負責從新渲染,執行render;getter是updateComponent。
  • 計算Watcher的update,負責標記dirty,告訴它數據不是最新的須要從新計算了;getter是計算屬性的get方法。
  • 偵聽器Watcher的update,負責執行回調方法,也就是watch的handler;getter是watch的取值表達式。
相關文章
相關標籤/搜索