提示:看到我 請讓滾去學習
vue3編譯模版的提升
文章目錄
- vue3編譯模版的提升
- 靜態節點提升
- 補丁標志和block的使用
- 附錄:
template explorer可以將我們的源模版轉化成渲染函數代碼,vue2中就有,而Vue3 template explorer 功能更加豐富。
靜態節點提升
當我們開啟靜態提升,可以發現靜態節點確實被從渲染函數中提升以便可以在每個渲染器上重用它,所以在每次組件更新時,我們都會重新調用render函數,但是_hoisted_1都將被重用。
這樣有兩點好處:
1.避免重新創建對象,然后刪除
2.在模版算法中,當看到兩個節點在同一位置時,在嚴格平等的情況下,我們可以跳過它,因為我們知道它永遠不會改變
補丁標志和block的使用
Vue 3 template explorer中當我們綁定一個監聽器,編譯器會生成一個補丁標志,表明這個節點有動態props需要修補下,以及需要修補的名稱是onclick。通常使用簡單的虛擬dom渲染算法,在div上綁定的元素,整個對象必須作為一個整體來diff,所以如果div上綁定了一個靜態的id屬性,我們還是會對比整個對象,確保它不會改變,因為運行時并不能確定它是否改變。但是vue3的編譯器通過補丁和關鍵字結合可以為運行時提供足夠信息,確定可以改變的是哪些屬性。所以渲染是可以跳過編譯器那些推斷的永遠不會改變的props。
上述在虛擬dom發生改變時,不會檢查整個節點的所有屬性方法,而是結合補丁標志檢查可改變的東西。但實際上,我們更多時候綁定一個事件監聽器,并不會更改事件處理程序,所以vue3默認開啟cacheHandlers
開啟cacheHandlers會把我們的事件變成一個內聯函數,并在第一次渲染時將其緩存,在此后渲染中,我們將始終使用同一個內聯處理程序,但是里面的函數會訪問ctx.onClick,所以即時onClick發生變化,我們不需要對vnode本身做任何事,所以上述代碼在更新過程中現在可以完全跳過整個節點。
所有這些在vue2父子組件中,即使子組件什么都沒有改變,也會導致所有子組件在父組件重新渲染時,所有接受到那個props的子組件重新渲染,在大型應用中,會引起連鎖反應,因為你在向下傳遞函數,在每次渲染時,都會創建一個新的內聯函數,會導致所有收到那個props的子組件重新渲染,所以vue3中使用句柄緩存,可以避免在大型組件樹中發生不必要的渲染
當我們調用render函數會生成一個類似以下的vdom
當vdom某些數據發生改變時,渲染器并不知道發生了什么改變,所以它會遞歸遍歷整棵樹,進行新舊快照對比,對比新舊節點找出改變了什么,這顯然會導致更多的性能問題。
例如上述代碼我們每次更新發生變化的只有span標簽,編譯器會使用_openBlock(),將模版的根變成我們所稱的塊(block),上圖中的_openBlock(),當塊打開時,所有的節點表達式都會被評估是否是動態的,當節點創建時攜帶有補丁表示的東西(動態)都會被跟蹤,并且添加到當前打開的block作為動態子節點。所以在render方法整個調用后,根div將有一個額外的屬性,稱為動態子節點,它是只包含了攜帶補丁標識的子節點的扁平化數組。不管dom節點層級多深,塊都只跟蹤其上的動態節點。
另外如果使用v-if等結構指令,它會改變節點結構,當v-if值切換時節點會從dom樹上消失,所以對于根節點的block,使用v-if的節點下的節點將不再安全。所以v-if節點會變成一個塊,這個塊會變成父節點的動態快被跟蹤,所以有嵌套的塊,將在扁平化數組中跟蹤他自己的動態子對象。
所以對于整個dom樹在跟新時不再需要再檢查每個vnode的變化,而是遞歸去找block,得到block內可能發生變化的信息。
補丁信息本身還編碼了關于什么樣工作信息,例如TEXT表示在你試圖區分這個節點時,只需要檢查它的文本內容而不需要關注例如props等其他信息
附錄:
vue2在線模版編譯器:[https://vue3js.cn/vue-template-explorer/]{.underline}
vue3在線模版編譯器:[https://template-explorer.vuejs.org/#]{.underline}