為什麼我們要了解 Vue 的渲染機制呢,當然是為了面試 當然是為了自我成長阿。
我主觀感受來說,在於 使用框架 這件事上,你不太應該要先了解渲染機制才能用框架,
這樣過於拉高使用門檻了,
但由於渲染機制、整個響應式系統,
以及明天的 Vapor Mode 有很大的關聯性,
因此還是在這邊去講述一下目前 Vue 是依賴什麼去機制去做內部渲染的。
Vue 到目前為止的渲染機制是基於 Virtual DOM 的概念構建的,
沒錯 Vue 也有 Vitrual DOM, 從 React 借 (ㄆㄧㄠˊ) 鑑 (ㄑ一ㄝˋ) 來的。
簡單來說就是先把所需要的 UI ,
透過程式生成 Vitrual DOM (本文接下來以將 vDOM 表示),
然後讓真實 DOM 與 vDOM 保持同步。
以下方為例 vDOM 可能會長這樣 :
const vnode = {
type:'div',
props:{
id:'ithome'
},
children:[
{type: 'p', children: 'ithelp' }
]
}
////////////// 同等於 //////////////////
<div id="ithome">
<p>ithelp</p>
</div>
這邊的 vnode
代表一個虛擬節點,裡面可能有更多的子節點,
接著渲染器就會遍歷整棵 vDOM tree,
下一步同步真實 DOM tree,這個過程就是 mount ( 掛載 ),
而當你在目前的基礎上更新了某個部分,例如說文字
那就會生成另一棵 vDOM tree,並和現在這份進行比較,
找出區別後把變化應用到真實 DOM 上,這個過程叫 patch( 更新 ) 或是 diffing, reconciliation
還記得我們上面的 vNode 嗎,那個看不是很懂的物件,
Vue 其實有提供一個 h()
讓你手動建立 vNode,
h()
是 hyperscript 的簡稱,指能生成 HTML 的 JavaScript,
又或著你可以叫他 createVNode()
。
<script>
import { h } from 'vue';
export default {
setup() {
return () => h('div', "It's Yanya.")
}
}
</script>
這邊可以簡單講解一下 h()
的幾個參數 :
完整的基礎寫法可以參照 基本用法 這邊不多做贅述。
那相信各位也看的出來,如果是很簡單的結構,使用渲染函數是沒關係
但還是原本的模板寫法更接近 HTML 結構。
對於一般使用者來說,模板語法應該能滿足大部分的場景,且 Vue 本身對於模板語法有靜態分析,
可以用來優化效能,例如更新類型標記。
<input id="name" :class="class">
這是一個綁著動態 class
的 input
為了要省去渲染器因為過多尋找變更,所造成的效能消耗,
Vue 選擇在編譯階段透過更新標記來優化。
render() {
return {
tag:'input'
props:{
id: 'name',
class: class
},
patchFlags: 1 // 假設是 1 代表 class 是動態的變數
}
}
這裡的 patchFlags
就是為了讓 rednerer 看到就知道只有這個 class
會變更,
讓編譯時能優化 vDOM 的效能,詳細可以看這裡 : 帶編譯時信息的虛擬 DOM
為了要讓瀏覽器看得懂我們所寫的程式碼,我們需要經過編譯,才能渲染到畫面上
那如果用超級簡易的架構來講的話,編譯流程會是這樣的,
這邊的原始碼就是我們的 <template>
,而目標語言就是 JavaScript,
因為瀏覽器的 V8 只看得懂 JavaScript。
那我們再補充渲染器的話會長怎麼樣呢
我們所撰寫的模板語法經過 compiler 編譯後,變成渲染函數,並掛在 script
區域。
如同我們上面所講的,渲染函數將會產生 vDOM,
而這個 vDOM 會經過 renderer 渲染成真實 DOM,這就是最簡易的渲染流程表示。
編譯器跟渲染器聽起來很難懂,但其實他們就只是一段程式碼,
那兩者內又各自做了甚麼呢?
編譯器:
<template>
進行詞法分析跟語法分析,得到模板 AST。渲染器:
AST (Abstract Syntax Tree) 抽象語法樹:
描述模板結構的一種語法樹,實際上就是一個 JavaScript 物件而已。
對整個渲染流程的討論就點到為止,再講下去會沒完沒了,
有興趣的朋友再去研究即可。
vDOM 在開發上帶來很大的方便,雖然說你更新某個部分他重畫一個 vDOM,
再去比較也會有效能耗損,但比起直接操作瀏覽器還是省去不少功夫。
這倒不是說使用 vDOM 就一定比操作真實 DOM 還快,
在少量變更情況下,不需要整個真實 DOM tree 重畫,那顯然快很多,
但如果全部數據同時更改,畫面大改,你覺得直接操作真實 DOM,
跟多畫一張 vDOM 然後比對,再去同步真實 DOM,真的有比較快嗎?
因為每次更新還是會遍歷整棵樹,即便某個分支從未改變,
還是要創建新的 vNode,這是常被人誤解的部分,
vDOM 還是多少會帶來性能消耗,也是被人詬病的一點。
而在 vue 3.6 所推出的 vapor mode 就是大幅的解決了這個問題,
他直接不依賴虛擬 DOM,而是更多的依靠 Vue 內部的響應式系統,
詳細的內容會在明天的 vapor mode 再做敘述。
Vue 2 ~ Vue 3 的渲染機制大部分都是依靠 vDOM 以及編譯的優化上,
因此蠻多篇幅反而是圍繞在 vDOM 在 Vue 中的實現。
個人覺得這部分比較難啃,原始碼也不是那麼好找好理解,
開發中也'暫時'沒遇到會需要基於渲染機制的知識才能解決的事。
也難保未來真的遇到就可以輕鬆寫意運用這些知識在實戰處理事情,
但多少試著去了解,說不定會發現一些讓你感到成就感的事。
明天我們就要進入響應式篇章的最後一篇,
也是 Vue 3.6 的主角 Vapor Mode 。
如果你喜歡這個系列或是想看我發瘋,歡迎按下 訂閱
一起走完這三十天吧。