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