關於一些Vue的文章。(7)

三毛丶發表於2017-04-22

原文連結我的blog,歡迎STAR。

首先安利一波福利,有沒有用vscode的小夥伴?推薦一個神奇的字型,自從用了這個字型,敲程式碼效率簡直上天了。先上圖看看效果:

關於一些Vue的文章。(7)
全等

關於一些Vue的文章。(7)
箭頭函式

關於一些Vue的文章。(7)
小於等於

關於一些Vue的文章。(7)

還有其他許多,就不一一列舉出來了。
有沒有看上了的?
沒有我等下再來問。

這次推薦的一篇文章來自,閱讀這篇文章有利於加深對Vue程式結構的瞭解,雖然是 1.0版本,不過好在 2.0 版本保留了絕大部分 1.0 的API。

在這篇文章裡我將是這幾個月來對 Vue 學習的一個小結。


思路

Vue 和其他的 MVVM 思路是類似的:

關於一些Vue的文章。(7)

主要是為了實現三個過程:

  1. Observer: 通過Observer對data進行監聽,並且提供訂閱某個資料項的變化的能力。利用Object.defineProperty, 將data裡的每個屬性全部轉化為getter/setter,已遍攔截物件賦值與取值操作;

  2. Compiler: 將template 解析為 render()方法;

  3. watcher: Compiler 的解析結果與 Observer 結合起來,在 Observer 監聽到資料發生改變時,接受通知,進而觸發 re-render, 重新渲染DOM。


new Vue

我們從 new Vue 開始,

關於一些Vue的文章。(7)

上圖即是官網給出的一張生命週期圖,主要是四個過程:

  • create: 在 new Vue() 時會執行,建立出 Vue 物件。
  • mount: 會根據 el, template, render 等,生成 Vnode, 完成 diff 演算法後掛載到 DOM 上。
  • updata: 當資料發生改變時,會重新渲染 DOM。
  • destory: Vue 銷燬時會執行。

現在,我們進入原始碼,分析具體的實現:

  • Create: 首先執行new vue()的時候,會進入_init
    其中關鍵部分的程式碼如下:
    關於一些Vue的文章。(7)

可以看出在 beforeCreatecreated 只有initState, initState 是用於實現data observerevent/watcher

  • Mount: 在_init最後,會執行 vm.$mount 方法:

關於一些Vue的文章。(7)

具體 vm.$mount 的分析,請看上篇。最後進行了 render(), 從而會有Vnode, 經過 DIFF 演算法後會有真實 DOM ;

  • Update: DOM 之後,會進行update方法:

      vm._watcher = new Watcher(vm, () => {
        vm._update(vm._render(), hydrating)
      }, noop)複製程式碼

將以上用一張序列圖表示也就是:

關於一些Vue的文章。(7)


深入響應式原理 (Observer, watcher)

MVVM 框架有一個很重要的特徵:就是當資料放生變化後,會自動更新對應的DOM節點。 Vue 是怎麼實現的?

關於一些Vue的文章。(7)

以上是來自官網的一張圖。

前面提到在 beforeCreatecreated 兩個生命週期鉤子函式之間會執行 initState() 方法。

initState() 原始碼裡:

關於一些Vue的文章。(7)

在這個方法裡,會對props, data, computed 等屬性利用 Object.defineProperty 將這些屬性全部轉化為 getter/setter

我們以 initData() 為例子進行分析:

關於一些Vue的文章。(7)

這裡有一個值得注意的地方, proxy(vm, "_data", keys[i]), 設定 vm._data為代理,具體作用是實現 vm.a === vm._data.a

initData()最後 會進行 observe(data, this)

observe()裡,既是轉化為 getter/setter

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      // 只有在有Dep.target時才說明是Vue內部依賴收集過程觸發的getter
      // 那麼這個時候就需要執行dep.depend(),將watcher(Dep.target的實際值)新增到dep的subs陣列中
      // 對於其他時候,比如dom事件回撥函式中訪問這個變數導致觸發的getter並不需要執行依賴收集,直接返回value即可
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          // 如果value在observe過程中生成了ob例項,那麼就讓ob的dep也收集依賴
          childOb.dep.depend()
        }
        if (isArray(value)) {
            //如果陣列元素也是物件,那麼他們observe過程也生成了ob例項
            dependArray(value)
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val
      if (newVal === value) {
        return
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      // observe 這個新資料
      childOb = observe(newVal)
      // 通知到dep 進行資料更新。
      dep.notify()
    }
  })複製程式碼

到這個時候,Observer 監聽已經完成,如果資料更新,我們會進行 dep.notify() 方法:

dep.notify() , 方法裡會執行update()

關於一些Vue的文章。(7)

update() , 中會進行
queueWatcher() 方法:

關於一些Vue的文章。(7)

queueWatcher(), 目的是通過 nextTicker 來執行 run()

run() 裡,其實就是執行 this.get() 方法:

關於一些Vue的文章。(7)

get() 方法裡,會執行 this.getter(), 方法來更新 DOM 。

this.getter()方法是啥?

再就是上文中所提到的new Watcher(), 的第二個引數。

vm._watcher = new Watcher(vm, () => {
  vm._update(vm._render(), hydrating)
}, noop)複製程式碼

而在 new Watcher 構造完成後,會呼叫 this.get() ,觸發 this.getter(),方法觸發 DOM 更新。

具體可以看watcher.js:

截一個關鍵程式碼部分的圖:

關於一些Vue的文章。(7)

以上,用一張序列圖來表示,既就是:

關於一些Vue的文章。(7)

對以上總結:

  • 首先 _init ,對屬性利用 Object.defineProperty,將屬性轉為 getter/setter,在 setter 方法裡,會呼叫 dep.notify()
  • vm 設定 new Watcher
  • data 變化時,進行資料跟新。

完。

相關文章