Vue 虛擬Dom 及 部分生命週期初探

踏入前端,步入玄學html

    17年末至18年初附帶作了vue的一些框架搭建,中途斷斷續續用了部分vue,時隔幾個月後的工做又拾起vue,對於一些原理性的知識淡忘了,正值這段時間使用中遇到了一些坑,又撥了部分代碼出來溫習溫習。前端

      正式進入大前端,新同事稱謂的玄學...vue

讀在最前面:node

  一、本文根據問題,講述大體 Vue虛擬Dom Diff 思路、數據響應式機制相關,源碼版本 Vue.js v2.5.17-beta.0element-ui

       二、知識點:vue virtual dom diff、 observe 、 watch、render ,各知識點暫不深刻剖析框架

  三、閱讀本文,讀者應瞭解Vue有必定的前端基礎,知道一些名詞概念如:render、vnode、virtual dom dom

  四、本文先引出問題及原理,而後單用2個實現給出詳情解析async

  四、v-if案例解析(element-ui  form-item表單沒法驗證問題剖析 )(問1)函數

  五、watch案例解析(element-ui el-select 沒法選中問題剖析)(問2)ui

 

直接甩疑問案例,Fire!

問1:當v-if爲true時,會從新渲染相關dom節點嗎?

問題來源(element-ui  form-item表單沒法驗證問題剖析)

<child v-if="true"><child>

答:會!(什麼??? 難道v-if 爲true不該該一直堅強到最後嗎,是什麼讓她如此虛弱)

涉及 知識點 vue virtual dom diff 

問題原理:

前言:vue2 引入了虛擬dom,即若是使用.vue的模板方式書寫,會首先編譯爲render函數,生成vnode,每次數據變動後,會在nexttick中進行新老vnode對比進行patch操做

一、對比先後 vnode對象相同顏色的節點進行同層級對比 ,以下圖

 

 

二、咱們挑選黃色節點說明:0、一、2標識節點序號 , 頭、尾 分別表明節點的頭部和節點的尾部

   對比序號從0、0(分別對應老節點序號0,新節點序號0)開始 ,每一次循環對比一個節點數據(使用sameVNode方法判斷,見下面代碼示例,源碼5858行),新老對比範圍爲 ,老(0-2)新(0-2)

(1)頭頭,即老0->新0 若是一致,則新老開始節點 序號 +1 同時,跳出循環 ,開始下一個循環對比 新老對比範圍爲 ,老(1-2)新(1-2)

(2)尾尾,即老2->新2 若是一致,則新老結束節點 序號 -1 同時,跳出循環 ,開始下一個循環對比 新老對比範圍爲 ,老(0-1)新(0-1)

(3)頭尾,即老0->新2 若是一致,則老開始節點 序號 +1,新結束節點序號-1  同時,跳出循環 ,開始下一個循環對比 新老對比範圍爲 ,老(1-2)新(0-1)

(4)尾頭,即老2->新0 若是一致,則老結束節點 序號 -1 ,新開始節點序號+1  同時,跳出循環 ,開始下一個循環對比 新老對比範圍爲 ,老(0-1)新(1-2)

(5)用新節點去未遍歷過的的老節點中查找

(6)老節點所有遍歷完或新節點所有遍歷完,則退出大循環(對未遍歷到的老節點執行刪除操做,對未遍歷到的新節點執行新增操做

function sameVnode (a, b) {
  return (
    a.key === b.key && (
      (
        a.tag === b.tag &&
        a.isComment === b.isComment &&
        isDef(a.data) === isDef(b.data) &&
        sameInputType(a, b)
      ) || (
        isTrue(a.isAsyncPlaceholder) &&
        a.asyncFactory === b.asyncFactory &&
        isUndef(b.asyncFactory.error)
      )
    )
  )
}  

 

大體如圖:

 問2:子組件中明明有watch value,爲何this.$emit('input', 888);沒有觸發watch回調,反而在父組件data數據變化後觸發回調?

問題來源(element-ui el-select 沒法選中問題剖析)

    <child v-model="b"></child> // b在父組件中沒有初始化,即沒有執行父組件的observe
    var child = Vue.extend({
      template: '<p>數據響應及render相關-案例說明</p>',
      props: {
        value: {
          required: true
        }
      },
      mounted() {
        this.$emit('input', 888);
      },
      watch: {
        value(val, oldVal) {
          console.log(val, oldVal, 'child')
        }
      }
    });

 涉及 知識點 observe、render、watch

問題原理:

一、vue實例在生成時,會經歷比較漫長的初始化過程如:初始化事件、渲染器、注入器、數據代理\劫持等等

二、v-model指令爲被默認構造 :value="b" @input="b = xxx" (此處b爲上面代碼中v-model="b"),執行this.$emit('input', 888),會更新b的值

三、每次data數據變化的時候,會收集watcher進入一個隊列,隊列中收集了全部此時刻週期中有相關變化造成queueWatcher,在下一個時間週期中進行數據響應更新 flushSchedulerQueue,並執行render 、patch

四、在render中會進行新老vnode patch,在更新過程當中會執行node的prepatch(源碼5938行)操做,其中會執行updateChildComponent -> validateProp 從而更新了 value的值,從而進入了watch回調

大體如圖:

    

 by:海豚灣-豐

相關文章
相關標籤/搜索