人云亦云,並不會讓你變得有多優秀,而會讓你越來越隨大流。
當你和別的開發在聊到 Vue 3.0 版本釋出,有哪些亮點時,你的答案之一肯定有“它變得更快了,效能上快了 1.2 ~ 2倍”。
那麼我就想問你,是什麼讓 Vue 變快了,尤大已經在 beta 版的線上直播上告訴了我們答案。
PatchFlag(靜態標記)
Vue 2.x 中的虛擬 DOM 是全量對比的模式,而到了 Vue 3.0 開始,新增了靜態標記(PatchFlag)。
在更新前的節點進行對比的時候,只會去對比帶有靜態標記的節點。並且 PatchFlag 列舉定義了十幾種型別,用以更精確的定位需要對比節點的型別。下面我們通過圖文例項分析這個對比的過程。
假設我們有下面一段程式碼:
<div>
<p>老八食堂</p>
<p>{{ message }}</p>
</div>
在 Vue 2.x 的全量對比模式下,如下圖所示:
通過上圖,我們發現,Vue 2.x 的 diff 演算法將每個標籤都比較了一次,最後發現帶有 {{ message }}
變數的標籤是需要被更新的標籤,顯然這還有優化的空間。
在 Vue 3.0 中,對 diff 演算法進行了優化,在建立虛擬 DOM 時,根據 DOM 內容是否會發生變化,而給予相對應型別的靜態標記(PatchFlag),如下圖所示:
觀察上圖,不難發現檢視的更新只對帶有 flag 標記的標籤進行了對比(diff),所以只進行了 1 次比較,而相同情況下,Vue 2.x 則進行了 3 次比較。這便是 Vue 3.0 比 Vue2.x 效能好的第一個原因。
我們再通過把模板程式碼轉譯成虛擬 DOM,來驗證我們上述的分析是否正確。我們可以開啟模板轉化網站,對上述程式碼進行轉譯:
上圖藍色框內為轉譯後的虛擬 DOM 節點,第一個 P 標籤為寫死的靜態文字,而第二個 P 標籤則為繫結的變數,所以打上了 1 標籤,代表的是 TEXT(文字),標記列舉型別如下:
export const enum PatchFlags {
TEXT = 1,// 動態的文字節點
CLASS = 1 << 1, // 2 動態的 class
STYLE = 1 << 2, // 4 動態的 style
PROPS = 1 << 3, // 8 動態屬性,不包括類名和樣式
FULL_PROPS = 1 << 4, // 16 動態 key,當 key 變化時需要完整的 diff 演算法做比較
HYDRATE_EVENTS = 1 << 5, // 32 表示帶有事件監聽器的節點
STABLE_FRAGMENT = 1 << 6, // 64 一個不會改變子節點順序的 Fragment
KEYED_FRAGMENT = 1 << 7, // 128 帶有 key 屬性的 Fragment
UNKEYED_FRAGMENT = 1 << 8, // 256 子節點沒有 key 的 Fragment
NEED_PATCH = 1 << 9, // 512
DYNAMIC_SLOTS = 1 << 10, // 動態 solt
HOISTED = -1, // 特殊標誌是負整數表示永遠不會用作 diff
BAIL = -2 // 一個特殊的標誌,指代差異演算法
}
hoistStatic(靜態提升)
我們平時在開發過程中寫函式的時候,定義一些寫死的變數時,都會將變數提升出去定義,如下所示:
const PAGE_SIZE = 10
function getData () {
$.get('/data', {
data: {
page: PAGE_SIZE
},
...
})
}
諸如上述程式碼,如果將 PAGE_SIZE = 10
寫在 getData
方法內,每次呼叫 getData
都會重新定義一次變數。
Vue 3.0 在這方面也做了同樣的優化,繼續用我們上一個例子寫的程式碼,觀察編譯之後的虛擬 DOM 結構,如下所示:
沒有做靜態提升前:
選擇 Option 下的 hoistStatic
:
靜態提升後:
細心的同學會發現, 老八食堂
被提到了 render
函式外,每次渲染的時候只要取 _hoisted_1
變數便可。認真看文章的同學又會發現一個細節, _hoisted_1
被打上了 PatchFlag
,靜態標記值為 -1 ,特殊標誌是負整數表示永遠不會用作 Diff。也就是說被打上 -1 標記的,將不在參與 Diff 演算法,這又提升了 Vue 的效能。
cacheHandler(事件監聽快取)
預設情況下 @click
事件被認為是動態變數,所以每次更新檢視的時候都會追蹤它的變化。但是正常情況下,我們的 @click
事件在檢視渲染前和渲染後,都是同一個事件,基本上不需要去追蹤它的變化,所以 Vue 3.0 對此作出了相應的優化叫事件監聽快取,我們在上述程式碼中加一段:
<div>
<p @click="handleClick">屋裡一giao</p>
</div>
編譯後如下圖所示(還未開啟 cacheHandler):
在未開啟事件監聽快取的情況下,我們看到這串程式碼編譯後被靜態標記為 8,之前講解過被靜態標記的標籤就會被拉去做比較,而靜態標記 8 對應的是“動態屬性,不包括類名和樣式”。 @click
被認為是動態屬性,所以我們需要開啟 Options 下的 cacheHandler
屬性,如下圖所示:
細心的同學又會發現,開啟 cacheHandler
之後,編譯後的程式碼已經沒有靜態標記(PatchFlag),也就表明圖中 P 標籤不再被追蹤比較變化,進而提升了 Vue 的效能。
SSR 服務端渲染
當你在開發中使用 SSR 開發時,Vue 3.0 會將靜態標籤直接轉化為文字,相比 React 先將 jsx 轉化為虛擬 DOM,再將虛擬 DOM 轉化為 HTML,Vue 3.0 已經贏了。
StaticNode(靜態節點)
上述 SSR 服務端渲染,會將靜態標籤直接轉化為文字。在客戶端渲染的時候,只要標籤巢狀得足夠多,編譯時也會將其轉化為 HTML 字串,如下圖所示:
需要開啟 Options 下的 hoistStatic
總結
以上便是 Vue3.0 在編譯時針對虛擬 DOM 的效能優化,這使得 Vue 3.0 在效能上是 Vue 2.x 的 1.2~2倍。
建立了一個 Vue3 的學習倉庫 vue3-examples,倉庫地址:https://github.com/newbee-ltd/vue3-examples,此倉庫將不定期更新各種 Vue3.0 相關的知識及各種整合 Demo 及 Vue3 使用小技巧,大家可以關注一下,有什麼建議也歡迎大家給我留言。
除註明轉載/出處外,皆為作者原創,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利。