vue2.x源碼中的占位符


vue2.x源碼中的占位符

事情的起因是我再次看了這篇掘金文章從一次 vue ssr 渲染客戶端報錯, 來看 ssr 客戶端激活過程,里面寫的在 updateClass() 中, vnode 的 tag 是 div, 而 vnode 的 elm 卻是 comment. 因為 comment 節點是沒有 setAttribute 方法的, 所以就報錯了.讓我看的不是很懂,特別是 comment 是干什么的我不是很懂,所以我搭了一個項目進行調試,理順我不清楚的地方。

通過本篇文章,您可以學到:

  1. 怎么搭環境調試 vue2.x 源碼?
  2. 占位符是什么?有什么用?
  3. 組件更新階段是怎么更新占位符節點的?

環境搭建

1.我們使用 vue-cli 搭建一個項目

vue create test-sourcecode

2.在項目里面克隆 vue 源碼庫

cd test-sourcecode
git clone git@github.com:vuejs/vue.git

3.進入 vue 源碼庫並使用 watch 形式編譯

cd vue-dev
npm install
npm run dev

4.回到 test-sourcecode 文件夾,加入.eslintignore文件,禁止 eslint 檢查 vue 源碼

cd ..
echo 'vue-dev' >> .eslintignore

5.把項目中的import Vue from 'vue'替換為import Vue from '../vue-dev/dist/vue.js'

6.使用npm run serve啟動項目即可。在源碼和項目上的改動都會有熱重載效果,可以放心加console.log看輸出了。

占位符是什么?有什么用?

我們經常在各種源碼分析文章上面看到占位符節點、placeholder節點等,他們是干什么的呢?

這里我就不貼圖或者代碼了,直接說了。vue 的組件在生成 vnode 的過程中,對於原生節點就直接生成 tag 為對應原生 tag 的vnode 節點,而對於組件節點就生成 tag 是vue-component-1-App的節點,這種節點就是占位符節點,或者叫 placeholder 節點。

這種節點的作用是,只是占據一個位置而已,並不會像原生vnode節點一樣會進行更新,也不會帶上對應組件vnode的數據,它的更新流程是這樣的,在比較新舊占位符節點進行更新的時候:

  1. 如果新舊占位符節點有不存在的,就直接創建或銷毀占位符節點,同時通過創建或通過vnode hook銷毀對應的組件 vnode。
  2. 如果新舊占位符節點都存在,但是不是同一個節點,就銷毀舊節點,在就占位符節點的父節點下創建新占位符節點,同時創建對應的組件 vnode。
  3. 如果新舊占位符節點都存在,並且是同一個節點,則使用vnode hook更新對應的組件 vnode。

注意:上面提到的組件 vnode 指的是,占位符對應的組件生成的組件 vnode,它才是真正控制這個組件更新的 vnode。(另外,函數式組件不會生成占位符節點,因為它沒有生命周期)

那怎么通過占位符節點找到對應的組件 vnode 呢?其實很簡單,在創建占位符節點的時候,會把組件 vnode 掛載到這個占位符節點的componentInstance屬性上,所以這個屬性就指向了組件 vnode。

組件更新節點是怎么更新占位符節點的

具體的更新流程上面已經說了,總的來說,就是使用占位符節點的 vnode hook 來更新組件 vnode ,這里詳細說明一下怎么用 vnode hook 來更新的

1.判斷這個節點是不是占位符節點,源碼中是通過查看占位符節點有沒有 hook 進行判斷的,如果是的話,就進行 prepatch:

if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
  i(oldVnode, vnode)
}

2.在進行 prepatch 的時候,會通過 componentInstance 拿到對應的組件 vnode,然后通過 updateChildComponent 方法更新組件 vnode 的props、listeners等

prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
    const options = vnode.componentOptions
    const child = vnode.componentInstance = oldVnode.componentInstance
    updateChildComponent(
        child,
        options.propsData, // updated props
        options.listeners, // updated listeners
        vnode, // new parent vnode
        options.children // new children
    )
},

3.到這里占位符節點的任務就已經完成了,但是組件只更新了props、listeners等,然后組件自身通過props、listeners這些響應式數據,來實現自身的更新

其它

在看源碼的時候,還是有幾個小問題的,留待下次解決了:

  1. 什么是comment 節點
  2. 什么是asyncPlaceholder?
  3. 什么是static vnode?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM