Vue3教程:Vue 3.x 快在哪裡?

人云亦云,並不會讓你變得有多優秀,而會讓你越來越隨大流。

當你和別的開發在聊到 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 已經贏了。

ssr.jpg

StaticNode(靜態節點)

上述 SSR 服務端渲染,會將靜態標籤直接轉化為文本。在客戶端渲染的時候,只要標籤嵌套得足夠多,編譯時也會將其轉化為 HTML 字符串,如下圖所示:

需要開啟 Options 下的 hoistStatic

總結

以上便是 Vue3.0 在編譯時針對虛擬 DOM 的性能優化,這使得 Vue 3.0 在性能上是 Vue 2.x 的 1.2~2倍。

創建了一個 Vue3 的學習倉庫 vue3-examples,倉庫地址://github.com/newbee-ltd/vue3-examples,此倉庫將不定期更新各種 Vue3.0 相關的知識及各種整合 Demo 及 Vue3 使用小技巧,大家可以關注一下,有什麼建議也歡迎大家給我留言。

除註明轉載/出處外,皆為作者原創,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。