Andy的前後端技術筆記、生活紀錄
VueJS設計實戰中的Imperative(命令式) vs Declarative(聲明式)以及Virtual DOM
發佈於: 2024-04-21 更新於: 2024-04-21 分類於: frontend > VueJS

VueJS設計實戰一書

先前不知道在哪個社群平台刷到有人在分享關於這本書,內容是介紹關於VueJS的設計概念,以喜歡了解更底層原理的我來說,就買來看看,重點是連Vue框架設計者Evan You都寫推薦,作者霍春陽也是Vue開發團隊的一員,這裡會是我讀完理解後的筆記,如果有興趣的人可以去買來看,描述算是輕鬆易懂,但是要有一定的熟悉VueJS、Javascript的基礎知識,不然可能還是不知道他在講什麼。

命令式與聲明式

Vue這個框架是屬於聲明式的,這兩個是什麼且又有什麼差異?

命令式:在書中的範例提到如下

1
2
3
4
5
6
7
8
9
// jquery版
$('#app') // 獲取div
.text('hello world') // 設定該tag的的文本內容
.on('click', () => { alert('ok'}) // 綁定點擊事件並跳出alert視窗

// 純Javascript
const div = documnet.querySelector('#app')
div.innerText = 'hello world'
div.addEventlistener('click', ()=>(alert('ok')))

聲明式(也是就是Vue的做法):

1
<div @click="() => alert('ok')">hello world</div>

這裡意思是說,我要建立一個div tag,並且在這裡綁定一個事件click,當點擊時跳出alert視窗。

在框架中希望的是寫成上述的樣式後由框架處理後續,把命令式這些行為給完成,這個好處是可以讓使用者更容易撰寫程式碼且心智負擔更小。

效能

前面提到的命令式與聲明式的寫法,但肯定有優缺點,聲明式的程式碼基本上不可能優於命令式的程式碼。

例子:
以命令式來說,如果要更改上面的div的內容只需直接以命令方式直接修改

1
div.textContent = 'hello Vue3'

上述已經是最極致效能的做法,也就是直接修改其內容

但在聲明式如Vue寫法要如何做?

1
2
3
4
<!-- 舊的 -->
<div @click="() => alert('ok)">hello world</div>
<!-- 新的 -->
<div @click="() => alert('ok)">hello vue3</div>

在框架中希望做到的是找到兩者個差異後只更新變化地方,但是最終要做的事還是與命令式一樣

1
div.textContent = 'hello Vue3'

這裡假設

  • 命令式修改內容的效能消耗 = A
  • 聲明式修改內容的效能消耗 = B + A

這個 B 指的是聲明式需要找出內容差異的效能消耗,而A是要修改DOM的效能消耗,與命令式是一樣的,所以以理論上來說聲明式的效能不會高過於命令式,因為聲明式程式碼封裝了命令式的程式碼,並且要找出內容差異的效能消耗。

那為什麼框架要選擇以聲明式的方式來寫呢?主要是為了程式碼的維護性,讓開發者更直觀地去開發。

Virtual DOM 虛擬DOM

為什麼要有虛擬DOM?

主要是如前面所說的B的部分,也就是希望儘可能減少 找出差異的效能消耗 ,如果可以盡可能減少就可以無限逼近命令式程式碼的效能。

前面提到的都是用如document.createElement()的方式來對DOM做操作,但在jQuery中是使用innerHTML的方式來操作頁面如下的方式

1
2
3
const html = '<div><span>hello world</span></div>'

div.innerHTML = html

這裡涉及最主要的一件事是要解析html字串變成DOM,DOM層面的計算是非常耗時的,DOM的運算會遠比Javascript的運算的效能來得差。
在書中有解釋到執行1000次建立js的物件的時間遠比使用document.createElement()的方式要快,主要是要表示建立DOM樹是很花時間的。

  • innerHTML的頁面建立效能:HTML字串拼接的計算量 + innerHTML的DOM樹建立的計算量

關於虛擬DOM的建立效能,第一步要建立JS物件,可以理解成對應真實DOM的描述物件,再來就是要走訪(遍歷)虛擬DOM樹來建立真實DOM,所以計算量如下:

  • 虛擬DOM的頁面建立效能:建立JS物件的計算量 + 訪問虛擬DOM並且建立真實DOM的計算量

效能量級

看上述兩者的頁面建立效能似乎差異不大,但是可以注意到使用innerHTML的方式是每次只要有修改內容時,整個舊的DOM就會被銷毀並且重新建立新的DOM樹。
而在虛擬DOM中,會是重新建立新的虛擬DOM樹,比較新舊的DOM樹並且找出有被修改的元素,對於真實的DOM樹部分只修改變動的地方。

可以看到在虛擬DOM中,除了前面提到的建立新的JS物件之外,還多做一件事情就是比較虛擬與真實DOM樹的差異,但因為都是在JS層面運算,相效於DOM層面運算的量級是小非常多的,而且在虛擬DOM中,只需要去修改真實DOM必須被修改的部分,效能與innerHTML相比,一下子就拉開差距。

在頁面越大對於innerHTML的效能就會越來越差,所以虛擬DOM的方式是一個非常好的選擇,不過要注意的是虛擬DOM的更新會因為資料變化量而影響到效能,也就是頁面中有越多的變化就會越吃效能。

心智負擔、維護性、效能

innerHTML虛擬DOM原生JavaScript
心智負擔中等心智負擔低心智負擔高
效能差效能不錯效能高

所以為心智負擔,意思是說對於開發者的開發體驗等等,不需要為了效能去寫原生JavaScript,也可以透過虛擬DOM來達到不錯的效能。

--- 到底拉 The End ---