天天看點

Vue2.x 源碼閱讀思路筆記Virtual DOMHTML parser建構一個最簡單的資料綁定的VueVNode 的屬性 attrs 和 props控制語句響應式原理深度追蹤依賴變化事件處理生命周期

目前公司主要技術棧是Vue,為了更好的使用,完成的了解Vue的原理是很有必要的。剛開始直接閱讀Vue源碼時,發現自己閱讀的效率很低。偶然間(其實不偶然)

在Github中發現了這份筆記 如何學習Vue2源碼,完整的記錄了實作Vue架構的完整過程。

我fork了作者的項目,跟着作者的思路如何學習Vue2源碼,完整走了一遍如何實作一個2.X版本的Vue。閱讀過程中我拉了一個新分支如何學習Vue2源碼(帶注釋),添加了詳細的注釋,友善自己記憶。通過閱讀作者的github,節省自己大量時間~

下面内容為自己的筆記,自己整理思路用。

建議閱讀這位大神的github:如何學習Vue2源碼。

我的閱讀記錄: 如何學習Vue2源碼(帶注釋)

Virtual DOM

參考:https://mp.weixin.qq.com/s/QXQAPEXojB9Zvri6GdXcJg
  • 生成virtual DOM
    • 用js對象模拟DOM樹(html文法樹?)
  • render真實DOM
    • virtual對象的render函數
  • 生成新的virtual DOM
  • render新的真實DOM
  • diff新舊真實DOM,儲存變更的patches
    • diff算法複雜度問題
    • 深度優先周遊,每周遊一個node同時跟新的樹比較,記錄差異
    • 差異類型,不同類型處理方式不一緻
    • 清單對比算法,調換順序最優解?
  • 在舊的真實DOM上應該patches
    • 不同的patch類型,不同的處理方式

HTML parser

  • Token解析
    • HTML字元串 --> Token數組
  • 文法樹解析
    • Token數組 --> HTML AST
  • VNode樹
  • Dom樹

首先有2個重要的概念要留意,将會貫穿後續的文章:靜态、動态(runtime)。

我們給一下以下簡單的定義:

靜态: 任何時刻運作轉換函數,同一個輸入得到的資料結構都是一緻的

動态: 存在某一時刻運作轉換函數,同一個輸入得到的資料結構是不一緻的

是以上邊所有涉及到的資料結構可以歸為:

字元串 是 靜态 的

HTMLElment Token 流 是 靜态 的

AST 樹 是 靜态 的

VNode 樹 是 動态 的

DOM 樹 是 動态 的

建構一個最簡單的資料綁定的Vue

AST樹、VNode render、VNode三者之間的漸進關系

Vue2.x 源碼閱讀思路筆記Virtual DOMHTML parser建構一個最簡單的資料綁定的VueVNode 的屬性 attrs 和 props控制語句響應式原理深度追蹤依賴變化事件處理生命周期

VNode 的屬性 attrs 和 props

Vue2.x 源碼閱讀思路筆記Virtual DOMHTML parser建構一個最簡單的資料綁定的VueVNode 的屬性 attrs 和 props控制語句響應式原理深度追蹤依賴變化事件處理生命周期

控制語句

v-if v-else-if v-else v-for

新增文法步驟同上及幾章:

  • parse階段增加解析v-for v-if部分的代碼,在AST上添加這部分文法的屬性
  • 生成render函數階段,同樣要增加将AST轉換成render函數的fn,
  • 生成Vnode階段,在單用render函數的時候,需要将v-if文法轉換為實際的VNode對象
  • path階段 将VNode樹轉換成DOM樹

響應式原理

基于Object.defineProperty實作data的資料監聽,set 寫操作的時候,我們要調用 vm._update() 進行視圖更新。

深入了解Vue的computed實作原理及其實作方式

深度追蹤依賴變化

obsever、dep、watch之間的關系:https://github.com/coderzzp/vue-come-true/tree/master/vue-come-true-First

深入淺出 - vue變化偵測原理

Vue2.x 源碼閱讀思路筆記Virtual DOMHTML parser建構一個最簡單的資料綁定的VueVNode 的屬性 attrs 和 props控制語句響應式原理深度追蹤依賴變化事件處理生命周期

詳細源碼分析:https://segmentfault.com/a/1190000014360080

  • 收集依賴流程:
observe -> 
walk -> 
defineReactive -> 
get -> 
dep.depend() -> 
watcher.addDep(new Dep()) -> 
watcher.newDeps.push(dep) -> 
dep.addSub(new Watcher()) -> 
dep.subs.push(watcher)
           
  • 視圖更新流程:
set -> 
dep.notify() -> 
subs[i].update() -> 
watcher.run() || queueWatcher(this) -> 
watcher.get() || watcher.cb -> 
watcher.getter() -> 
vm._update() -> 
vm.__patch__()
           

事件處理

時間的處理與之前屬性的處理基本一緻:

a. 以下 HTML:

b. 解析後得到的 AST 節點:

evtAstElm = {
  type: 1,
  tag: 'button',
  events: {
    'click': { value: 'clickme' }
  },
  children: [ /* blabla.. */ ]
}
           

c. 生成的 render code:

_c('button', { 
  on: { "click": clickme } 
}, [ _v("click me")] )
           

d. 得到一個帶屬性的 VNode 節點:

VNode {
  tag: 'button',
  data: {
    on: { "click": clickme } 
  },
  children: [ /* blabla.. */ ]
}
           

c. 生成的 render code ( clickme 函數需要代理到目前的 vm 對象上,同時綁上 vm 這個運作時 context):

_c('button', { 
  on: { "click": clickme } 
}, [ _v("click me")] )
           

e. 最後渲染在 dom 上的時候:

源碼的事例 https://github.com/raphealguo/how-to-learn-vue2-blob/blob/master/articles/2.4.1.md

生命周期

Vue2.x 源碼閱讀思路筆記Virtual DOMHTML parser建構一個最簡單的資料綁定的VueVNode 的屬性 attrs 和 props控制語句響應式原理深度追蹤依賴變化事件處理生命周期
  1. new Vue()
  2. 初始化生命周期、往vue執行個體上綁事件、方法

    這裡隻的事件、方法,并不是使用者自己綁定的method、data之類的資料,而是Vue執行個體本身的一些内部方法。

  3. 執行鈎子:

    beforeCreate()

    beforeCreate()

    鈎子中無法通路到,data、method、$el等資料

    $el挂載點,也就是vue生成的dom挂載的地方

  4. 初始化狀态

    簡單來說就是初始化method、data,監聽資料之類的操作

  5. 執行鈎子:

    created()

    data、method方法已經能通路了,但是還沒有挂載點資訊
  6. 是否有挂載點?

    有:擷取模闆資訊(有無template,兩種情況),

    編譯 -> vnode -> render函數 -> Dom -> diff -> patch

    無:暫停執行,知道使用

    .$mount()

    手動挂載後,繼續執行
  7. 執行鈎子:

    beforeMount()

    .$el

    中已經有挂載點了,但是dom資源還沒有渲染
  8. 将VNode渲染成Dom,并挂載到挂載點上。

    $el

    得到更新。
  9. 執行鈎子

    mounted()

    挂載完成。
  10. data改變時,觸發監聽。
  11. 執行

    beforeUpdate()

    此時僅監聽到了data改變,還未做出操作
  12. diff VNode -> patch

    對比VNode變化,更新dom
  13. 檢測到頁面銷毀(

    .$destroy()

    被執行)
  14. 執行鈎子

    beforeDestroy()

  15. 移除data、mothod、watcher、components、event listener等
  16. 執行鈎子

    destroyed()

繼續閱讀