操作dom影響性能的原因


為什么dom操作會影響性能?

在瀏覽器當中,dom的實現和ECMAScript的實現是分離的。

例如,在IE中,ECMAScrit的實現在jscript.dll中,而DOM的實現在mshtml.dll中;在Chrome中使用WebKit中的 WebCore處理DOM和渲染,但ECMAScript是在V8引擎中實現的,其他瀏覽器的情況類似。

因此,操作dom,就是通過js代碼調用dom的接口,就相當於兩個相互獨立的模塊發生了交互。這樣,相比於在同一個模塊當中互相調用,這種跨模塊的調用它的性能損耗是非常高的。

然而,dom操作影響性能最主要是因為它導致了瀏覽器的重繪(repaint)和重排(reflow)。

瀏覽器渲染原理

為了可以更加深刻地理解重繪和重排對性能的影響,需要簡單了解一下瀏覽器的渲染原理。

從下載文檔到渲染頁面的過程中,瀏覽器會通過解析HTML文檔來構建DOM樹,解析CSS產生CSS規則樹。JavaScript代碼在解析過程中, 可能會修改生成的DOM樹和CSS規則樹。之后根據DOM樹和CSS規則樹構建渲染樹,在這個過程中CSS會根據選擇器匹配HTML元素。渲染樹包括了每 個元素的大小、邊距等樣式屬性,渲染樹中不包含隱藏元素及head元素等不可見元素。最后瀏覽器根據元素的坐標和大小來計算每個元素的位置,並繪制這些元 素到頁面上。無論何時總會有一個初始化的頁面布局伴隨着一次繪制。

重繪

重繪,就是指頁面某些部分需要重新繪制,由於節點的幾何屬性發生改變或者由於樣式發生改變,例如改變元素背景色時,屏幕上的部分內容需要更新,而元素的位置和尺寸並沒有改變。

重排

元素的位置或尺寸發生了改變,瀏覽器需 要重新計算渲染樹,導致渲染樹的一部分或全部發生變化。渲染樹重新建立后,瀏覽器會重新繪制頁面上受影響的元素。

也就是說,重排,改變的是dom文檔的結構,例如dom元素的位置或者尺寸發生了變化,需要重新布局,就會發生重排。

優化方法:

1、將dom操作積累起來作批量操作

現代瀏覽器中會有優化方法,就是把dom操作積累起來,做批量處理。但是在有些情況下,瀏覽器會立即重排或重繪。比如請求如下的DOM元素布局信息:offsetTop/Left/Width/Height、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()或 currentStyle。因為這些值都是動態計算的,所以瀏覽器需要盡快完成頁面的繪制,然后計算返回值,從而打亂了重排或重繪的優化。

2、合並多次的DOM操作為單次的DOM操作

最常見頻繁進行DOM操作的是頻繁修改DOM元素的樣式,代碼類似如下:

element.style.borderColor = '#f00';
element.style.borderStyle = 'solid';
element.style.borderWidth = '1px';

這種編碼方式會因為頻繁更改DOM元素的樣式,觸發頁面多次的重排或重繪,上面介紹過,現代瀏覽器針對這種情況有性能的優化,它會合並DOM操作,但並不是所有的瀏覽器都存在這樣的優化。推薦的方式是把DOM操作盡量合並,如上的代碼可以優化為:

// 優化方案1
element.style.cssText += 'border: 1px solid #f00;';
// 優化方案2
element.className += 'empty';


示例的代碼有兩種優化的方案,都做到了把多次的樣式設置合並為一次設置。方案2比方案1稍微有一些性能上的損耗,因為它需要查詢CSS類。但方案2的維護性最好,這在上一章曾經討論過。很多時候,如果性能問題並不突出,選擇編碼方案時需要優先考慮的是代碼的維護性。

類似的操作還有通過innerHTML接口修改DOM元素的內容。不要直接通過此接口來拼接HTML代碼,而是以字符串方式拼接好代碼后,一次性賦值給DOM元素的innerHTML接口。

3、使用文檔片段

文檔片段是一個輕量級的document對象,並不會和特定的頁面關聯。通過在文檔片段上進行DOM操作,可以降低DOM操作對頁面性能的影響,這 種方式是創建一個文檔片段,並在此片段上進行必要的DOM操作,操作完成后將它附加在頁面中。對頁面性能的影響只存在於最后把文檔片段附加到頁面的這一步 操作上。代碼類似如下:

var fragment = document.createDocumentFragment();
// 一些基於fragment的大量DOM操作
...
document.getElementById('myElement').appendChild(fragment);

4、通過設置DOM元素的display樣式為none來隱藏元素

這種方式是通過隱藏頁面的DOM元素,達到在頁面中移除元素的效果,經過大量的DOM操作后恢復元素原來的display樣式。對於這類會引起頁面重繪或重排的操作,就只有隱藏和顯示DOM元素這兩個步驟了。代碼類似如下:

var myElement = document.getElementById('myElement');
myElement.style.display = 'none';
// 一些基於myElement的大量DOM操作
...
myElement.style.display = 'block';

5、克隆DOM元素到內存中

這種方式是把頁面上的DOM元素克隆一份到內存中,然后再在內存中操作克隆的元素,操作完成后使用此克隆元素替換頁面中原來的DOM元素。這樣一來,影響性能的操作就只是最后替換元素的這一步操作了,在內存中操作克隆元素不會引起頁面上的性能損耗。代碼類似如下:

var old = document.getElementById('myElement');
var clone = old.cloneNode(true);
// 一些基於clone的大量DOM操作
...
old.parentNode.replaceChild(clone, old);

6、設置具有動畫效果的DOM元素的position屬性為fixed或absolute

把頁面中具有動畫效果的元素設置為絕對定位,使得元素脫離頁面布局流,從而避免了頁面頻繁的重排,只涉及動畫元素自身的重排了。這種做法可以提高動 畫效果的展示性能。如果把動畫元素設置為絕對定位並不符合設計的要求,則可以在動畫開始時將其設置為絕對定位,等動畫結束后恢復原始的定位設置。在很多的 網站中,頁面的頂部會有大幅的廣告展示,一般會動畫展開和折疊顯示。如果不做性能的優化,這個效果的性能損耗是很明顯的。使用這里提到的優化方案,則可以 提高性能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM