为什么需要虚拟DOM?
目錄
- 1. 真實DOM操作的性能問題
- 2. 虛擬DOM的作用
- 總結
我們知道,虛擬DOM的概念是由Facebook的React團隊最早提出來的,也是React框架的核心概念之一。它的作用是以js的形式在內存中描述真實的DOM結構,這樣當頁面內容需要發生變動時,React可以通過對前后虛擬DOM的比對,計算出如何以最小的代價操作真實DOM。
1. 真實DOM操作的性能問題
在虛擬DOM出現之前,前端開發者最常用的方式是用jQuery直接操作真實DOM,像下面這樣:
<body><div id="root"></div><script>$('#root').text('這是一段文本');// 或者這種原生的方式// var root = document.querySelector('#root');// root.textContent = '這是一段文本';</script> </body>在本例中,jQuery只是提供了一層對原生DOM操作的封裝。我們可以簡單手寫一個$實現:
function $ (selector) {this.nodeList = document.querySelectorAll(selector);return this; }$.prototype.text = function (textContent) {this.nodeList.forEach(node => {node.textContent = textContent;});return this; }(本例只是用于解釋jQuery的封裝原理,并非jQuery的真實實現)
通過這個簡單的封裝我們可以發現,jQuery的主要工作是提供對原生DOM操作的統一實現(另外還有對原生js的增強,如ajax、動畫等)。換句話說,使用jQuery,本質上還是在直接操作真實DOM。
那么直接操作真實DOM有什么問題呢?
這就要從瀏覽器內核的構成說起了。我們以webkit內核為例,它大致包含以下模塊(圖片來自網絡):
首先來看左側的深色區域,它是webkit內核的WebCore層。該區域左下角的HTML模塊代表HTML引擎,作用是解析HTML文檔。當瀏覽器下載了一個HTML文檔后,它負責將該文檔解析成DOM樹,也就是由一個個標簽節點構成的文檔樹。最終解析出來的DOM樹將交由它右側的DOM模塊負責管理,而這個DOM樹就是我們平常所說的真實DOM。
在深色區域的右側緊鄰著的是JavaScriptCore,即webkit默認的JavaScript引擎(在Chrome和chromium中它被替換為了V8),它負責執行JavaScript代碼。
我們知道,JavaScript具備操作DOM樹的能力。但是從瀏覽器內核的結構可以看到,DOM樹由DOM模塊負責管理,在瀏覽器內核中單獨占有一塊內存,而這塊內存與JavaScript引擎所管理的內存并無直接關系。換句話說,JavaScript引擎不能直接操作真實DOM樹。
為了給JavaScript提供操作DOM樹的能力,瀏覽器在全局對象window上為JavaScript封裝了一個document對象,然后在該對象上提供了大量的DOM操作接口,這些接口都是用C++實現的。如:
在瀏覽器控制臺查看document.getElementById,可以看到它的函數體是{ [native code] },這表示它是一個用C++編寫的函數,因此無法查看具體實現。當我們在調用這個函數時,JavaScript引擎并沒有直接與DOM模塊交互,而是由瀏覽器來操作DOM模塊,隨后再把操作結果返回給JavaScript引擎。這種借助父級模塊實現兩個同級模塊交互的通信方式非常常見。
正是由于JavaScript需要借助瀏覽器提供的DOM接口才能操作真實DOM,所以操作真實DOM的代價往往是比較大的(這其中還涉及C++與JavaScript數據結構的轉換問題)。再加上修改DOM經常導致頁面重繪,所以一般來說,DOM操作越多,網頁的性能就越差。我們以一個簡單的圖例來理解這個過程:
有人可能會問,既然DOM操作的代價如此之大,為什么不由JavaScript引擎來管理DOM呢?不要忘了,世界上第一款瀏覽器誕生于1990年,那時候就已經出現了DOM的原始概念,而JavaScript則直到1995年才誕生。換句話說,DOM的出現早于JavaScript。因此,如果要由JavaScript來管理DOM,那就意味著瀏覽器內核必須重構。而讓瀏覽器開發者為了一個早期被稱為“玩具語言”的JavaScript重構瀏覽器內核顯然是不可能的。不過隨著JavaScript的發展,由JavaScript引擎直接管理DOM的構想已經被提上了chromium的計劃列表(目前只是定為長期計劃,要真正實現還需要相當長的時間)。
所以,截止到目前,如何有效地減少對真實DOM的操作,仍然是前端性能優化的一個關鍵點。虛擬DOM就是目前較為流行的一個解決方案。
2. 虛擬DOM的作用
顯然,JavaScript無法直接操作DOM是帶來上述性能問題的根源之一(其他原因包括,真實DOM樹的體積非常龐大,而且操作它會導致頁面重繪)。那么能不能在JavaScript內存中,以js對象的形式也描述一棵DOM樹呢?當然可以,這就是我們所說的虛擬DOM樹。
可以看到,虛擬DOM并不能消除原生的DOM操作,你仍然需要通過瀏覽器提供的DOM接口來操作真實DOM樹,才能使頁面發生改變。虛擬DOM的設計似乎是多此一舉。
但是虛擬DOM帶來了一個重要的優勢,那就是我們可以在完全不訪問真實DOM的情況下,掌握DOM的結構,這為框架自動優化DOM操作提供了可能。舉例來說,如果我們本打算手動進行三次真實DOM操作,而框架在分析了虛擬DOM的結構后,把這三次DOM操作簡化成了一次,這不就帶來了性能上的提升嗎?再甚者,如果連這一次DOM操作都可以由框架自動完成(自動更新的前提是我們要定義視圖和數據的綁定關系),而我們只需要負責處理數據,那么虛擬DOM的價值體現得就更明顯了。
React就是這么使用虛擬DOM的。
當我們使用jsx語法定義一個模板時,React會用它生成一個由JavaScript描述的虛擬DOM樹,并將其保存在JavaScript內存中。這個虛擬DOM樹還保留了我們在模板中定義的數據和視圖的綁定關系,這為React自動根據數據變化更新視圖提供了可能。隨后當數據發生變化時,React重新生成一個虛擬DOM樹,通過對比兩個虛擬DOM樹的差異,React就可以知道該如何高效地更新視圖。接著它就會調用原生的DOM接口,去更新真實DOM。過程大致如下:
對于開發者而言,虛擬DOM的實現是透明的,它只是框架自動高效更新DOM的一種內部解決方案。開發者需要按照框架給定的語法定義數據和視圖的綁定關系,隨后就只需要關心數據變化(即業務邏輯)即可。
當然了,虛擬DOM并不是解決DOM操作性能問題的唯一解決方案,Vue的響應式系統也是一種重要的解決方案。從某種程度上來說,Vue依靠響應式系統可以實現“精確定點更新”,即直接定位到哪個DOM節點需要更新,而不需要經過虛擬DOM的比對,不過“精確定點更新”的內存代價偏大,因此目前Vue采用了響應式系統和虛擬DOM結合的方式,本文暫不詳述。
最后我們來看一下Vue中虛擬DOM樹的結構,實際上它就是一個js對象而已,我們以下面的模板為例:
<template><div id="app"><ul><li v-for=“item in items”>itemid: {{ item.id }}</li></ul></div> </template>對應的虛擬DOM:
總結
要理解為什么需要虛擬DOM,必須弄清楚JavaScript引擎和DOM模塊之間的關系,并體會由這種關系導致的DOM操作的性能問題。虛擬DOM設計的核心就是用高效的js操作,來減少低性能的DOM操作,以此來提升網頁性能。
從一定程度上來說,是瀏覽器的架構問題催生了虛擬DOM,而這個架構問題幾乎需要重構瀏覽器內核才能解決,所以目前虛擬DOM仍廣為流行。如果未來的某一天,真實DOM被遷移到JavaScript內存中,虛擬DOM的價值實際上也就不存在了。
總結
以上是生活随笔為你收集整理的为什么需要虚拟DOM?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 昆明最快dns服务器地址,手机网速最快的
- 下一篇: Python之复制的cookie参数处理