背景
依舊接著說面試那點事,最近面試了很多人,面對曾經用過 vue
的候選人,我都會問一個問題:Vue
為什麼要在 2.x
之後切換到 虛擬 DOM
上?
而在這個過程中我發現了很多人都會陷入一個誤區:** 選擇一個技術一定是因為這個技術是好的、優秀的,所以才會用它。** 這個理解實際上也並不算錯,但是我認為也是不妥的,很多時候選擇一個技術真不是因為它多好,而是因為它能解決你當前碰到的問題,所以哪怕它並不完美,甚至並不好,可是隻要能解決問題,並且暫時並沒有其它更好的解決方案,同時帶來的後果能在承受範圍之內,那麼就可以採用它。很顯然,虛擬 DOM 就是一個這樣的東西。
虛擬 DOM 真的快嗎?
非常多的人在說到虛擬 DOM 都會說虛擬 DOM 的 diff 演算法如何如何,怎麼提升效能如何如何,但事實上只要仔細思考一個問題你就能知道這個結論是站不住腳的,至少在相當多情況下是站不住腳的:虛擬 DOM
透過 diff
演算法找到了發生變化後的節點,它是不是依舊要操作 真實 DOM
修改?那麼它憑什麼就比直接修改 真實 DOM
更快呢?(顯然,它多了一層 diff
操作)。
當然很多人會提到,虛擬 DOM
可以批次在記憶體操作,然後把結果輸出到瀏覽器,大量修改的情況下效能更優秀,可以保證大量操作下的效能下限,真實操作 DOM
每次都會引起重繪,不如 虛擬 DOM
批次替換效率高。看起來確實是這樣,可是事實上呢?首先,並不會有人無聊到真的重複更新大批次資料吧?為了一個極少出現的場景放棄了大多數的場景,值得麼?相反,大多數正常操作下,本來只需要修改兩個簡單的 DOM
,而 虛擬 DOM
還要再完整走一遍 diff
演算法,才找到這兩個節點,再去替換,這是否又拉低了效能呢?更何況 虛擬 DOM
一直有一個解決不了的問題就是:他根本不知道什麼時候該更新,什麼時候不該更新,也是因為這個原因,React
才提供了 ShouldComponentUpdate
讓使用者自己控制是否要更新。更更何況,虛擬 DOM 做的這個合併操作,其實手動也是可以操作的,我想做 Canvas
的人應該大有體會,在記憶體裡生成一個虛擬的 Canvas
,然後進行多次繪製,之後一次性將這個 Canvas
渲染到頁面上。事實上,React
官方並不是很在意這個效能,比如 React
自己的 虛擬 DOM
演算法都不是最優秀的(虛擬 DOM 的演算法相當多),隨便列舉幾個:
- GitHub - Matt-Esch/virtual-dom: A Virtual DOM and diffing algorithm
- GitHub - joelrich/citojs
- GitHub - localvoid/kivi: UNMAINTAINED Javascript (TypeScript) library for building web user interfaces
- GitHub - infernojs/inferno: An extremely fast, React-like JavaScript library for building modern user interfaces
其中 inferno 的速度更是快得離譜。
這足以說明,React
其實是用 虛擬 DOM
根本不是因為快,或者換個說法,虛擬 DOM
的效能並不是 React
的首要考慮,當然也並不是說完全就不考慮效能,inferno
的作者似乎已經進入 React 團隊。更願意說:虛擬 DOM 只是 React 為了實現目的而選擇的一條最合適的路,他只是手段,而不是目的,他的 DOM 操作最佳化也只是避免效能損耗的手段,而不是 React 的根本目的。
當然這並不是說 虛擬 DOM
就沒有優點,優點也是很明顯的,雖然我們能夠手動合併 DOM
,可是這畢竟是一個繁瑣的操作,如果有框架能幫助我們合併,又何必去手動操作呢?在少量 DOM
更新的情況下,即使 虛擬 DOM
會拖累速度,事實上也並不會對體驗有什麼影響,反倒是在大量操作的情況下保障了效能下限,會讓我們的應用可用性更穩定。
Ok,接著回到 Vue
,Vue
事實上是有依賴收集這個過程的,換句話說,其實 Vue
在依賴收集階段,完全可以精確的收集到頁面哪些 DOM
使用了資料,並且可以在資料變化的時候精確的更新這些 DOM
,這個效能並不會很差,Vue1
一直是這麼幹的,而 Vue1
長期標榜自己的效能會比 React
更加優秀也證明了這點,對於 Vue
而言,似乎直接精準收集依賴,精準更新更加的自然,使用虛擬 DOM 看起來更像是脫褲子放屁:我明明可以知道哪個 DOM
更新,依然要裝作我不知道,然後走一遍 diff 演算法才發現,哦,原來是這幾個 dom 更新了。這裡提一嘴,Atom
和 VsCode
都是 js
製作的,其中 Atom
曾經使用過 虛擬 DOM
,Atom
是個什麼效能大家都清楚,最終轉為 真實 DOM
操作,VsCode
則是一直使用 真實 DOM
,編輯器場景其實和 vue 很像,編輯器當然準確知道你操作了哪一行哪一個文字,但是如果他依舊裝作不知道要走一遍虛擬 dom,這就正如之前說的,完全是脫褲子放屁的無意義操作。所以不要天真地以為 虛擬 DOM
就是快,就是好,就是一定要上的,虛擬 DOM
並不是完全沒有代價的。
虛擬 DOM 是否還有其他優點?
正如之前所說,虛擬 DOM 其實並不見得比手動快,但是他卻提供了一個非常重要的特性:可以接受 Parser 解析轉化。這意味著其實相當多的東西我們都可以在編譯階段解決,比如:xss 攻擊($$typeof)、合併 DOM 操作、跨平臺 等等。
其中我認為最重要的就是跨平臺,平臺不單止 Web
、ios
等,更是止程式語言,比如考慮這樣一個情況:我們是否可以編寫一套轉換器,用來把 python
轉換為 js
?這其中比較麻煩的就是 DOM
,因為 DOM
是 js 獨有的東西,可是擁有 虛擬 DOM
之後我們可以在 python
也實現一套,之後轉為 虛擬 DOM
這樣一個統一的抽象格式,這樣不就實現了其他程式語言平臺來編寫前端程式碼?事實上 ssr(服務端渲染)
就是這個原理,node
是沒有 DOm
的,透過虛擬 DOM
來抽象即可達到操作 DOM
的目的,同時我們在 jsx
裡使用函式來編寫宣告式程式設計來編寫原本命令式的 DOM
操作也是得益於此。再比如我們是否可以把前端平臺程式碼移植到其他平臺?比如 React Native
、Weex
,比如數量繁多的小程式框架。
結論
所以我一直認為,虛擬 DOM
的真實意義不是他的效能,而是他提供了無限的可能,他是對 真實 DOM
的抽象,你可以用一門全新的語言寫下程式碼,抽象為 虛擬 DOM
,然後再透過社群其他成員編寫完成的 虛擬 DOM
庫,來轉為各種平臺(ios
、android
)的頁面,只要有一個框架實現這個轉換,其他框架都可以享受這個紅利。
本作品採用《CC 協議》,轉載必須註明作者和本文連結