原文地址--->http://developer.51cto.com/art/201504/473422.htm
最近做項目時遇到了頁面加載卡頓問題,一時沒有頭緒,感到無從下手,看到這篇文章,所以整體梳理了一下,在此記錄。
在富客戶端網頁應用中,界面上的UI的更改是通過DOM操作實現的。
盡管DOM提供了豐富接口供外部調用,但是dom操作的代價很高,
頁面前端代碼的性能瓶頸大多集中在DOM操作上,因此,前端性能優化的
一個主要的關注點是dom操作的優化,因此我們可以想辦法通過盡量減少
DOM操作來優化性能。
首先,DOM操作為什么會影響性能。在瀏覽器中,DOM的實現和ECMAScript
的實現是分離的。例如,在IE中,ECMAScript的實現在jscript.dll中,而DOM的
實現在mshtml.dll中;在chrome中使用webkit的WebCore處理DOM和渲染,但
ECMAScript是在V8引擎中實現的,其他瀏覽器的情況類似,所以通過Javascript
調用dom接口,是相當於兩個模塊的交互。相比較在同一模塊中的調用,這種跨模塊的
調用其性能損耗是很高的,但DOM操作對性能影響最大是因為它導致了瀏覽器的重繪
和重排。
瀏覽器渲染原理的簡單說明
從下載文檔到渲染頁面的過程中,瀏覽器會通過解析HTML文檔來構建DOM樹,解析
CSS產生CSS規則樹。javascript在代碼解析的過程中,可能會修改生成的dom樹
和css規則樹,之后根據dom樹和css規則樹構建渲染樹,在這個過程中css會根據
選擇器匹配HTML元素。渲染樹包括了每個元素的大小,邊距等樣式屬性。渲染樹
中不包含隱藏元素及head元素等不可見元素。最后瀏覽器根據元素的坐標和大小
來計算每個元素的位置,並繪制這些元素到頁面上。重繪指的是元素的位置或尺寸
發生了改變,瀏覽器會重新繪制頁面上受影響的元素,重排的代價高於重繪。
之下的DOM操作會導致重繪或重排:
1.增加,刪除和修改可見DOM元素
2. 頁面初始化的渲染
3.移動dom元素
4.修改css樣式,改變dom元素的尺寸
5.dom元素內容改變,使得尺寸被撐大
6.瀏覽器窗口尺寸改變
7.瀏覽器窗口滾動
現代瀏覽器會針對重排或重繪做性能優化,例如,把DOM操作積累一批后統一
做一次重排或重繪,但在有些情況下,瀏覽器會立即進行重排或重繪,比如請求如下的
dom元素布局信息
offsetTop/Left/Width/Height
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
getComputeStyle()或currentStyle
因為這些值都是動態計算的,所以瀏覽器需盡快完成頁面繪制,計算返回值,打亂了重繪或
重排優化。
可以遵循一些最佳實踐來降低影響-->
方法一:合並多此dom操作為單次dom操作
通過class類名來元素的大量樣式更改,代碼維護性較好。
通過innerHTML接口來修改DOM元素的內容時,以字符串方式拼接好代碼后,一次性賦值給
DOM元素的innerHTML接口。
方法二: 把DOM元素離線或隱藏后修改
把元素從頁面流中脫離或隱藏,這樣處理后,只會在DOM元素脫離或添加時,或者是隱藏或顯示時才會造成頁面的重繪會重排,對脫離了頁面布局的DOM元素操作就不會導致頁面的性能問題。
這種方式適合需要大批量修改dom元素的情況。具體方式由三種:
(1) 使用文檔片段
文檔片段是一個輕量級的document對象,並不會和特定的頁面關聯,通過在文檔片段上進行DOM操作,可以降低DOM操作對頁面性能的影響,這種方式是創建一個文檔片段,並在此片段上
進行必要的DOM操作,操作完成后將它附加在頁面中,對頁面的影響只存在於最后把文檔片段附加到頁面的這一步操作上。
var fragment=document.createDocumentFragment();
//一些基於fragment的大量dom操作
...
document.getElementById('myElement').appendChild(fragment);
(2)隱藏元素
通過隱藏元素,達到在頁面上移除元素的效果,經過大量的DOM操作后恢復元素原來的display樣式,只有隱藏和顯示元素時會引起頁面重繪或重排操作。
var myElement=document.getElementById('myElement');
myElement.style.display='none';
//dom操作
myElement.style.display='block';
(3)克隆DOM元素到內存中
把頁面上的DOM元素克隆一份到內存中,然后在內存中操作克隆的元素,操作完成后使用此克隆元素替換頁面中原來的DOM元素,只有替換元素時會影響性能,在內存中操作克隆元素不會引起頁面上的性能損耗。
var old=document.getElementById('myElement');
var clone=old.cloneNode(true);
//dom操作
old.parentNode.replaceChild(clone,old);
3.設置具有動畫效果的DOM元素的position屬性為fixed或absolute
把頁面中具有動畫效果的元素設置為絕對定位,使得元素脫離頁面布局流,從未避免了頁面頻繁的重排,只涉及動畫元素自身的重排。這種做法可以提高動畫效果的展示性能。(在動畫開始時將其設置為絕對定位,等動畫結束后恢復原始的定位設置)。
4.謹慎獲得dom元素的布局信息,變量本地化。
把獲取到的元素的布局信息值緩存在局部變量中。在有大量的DOM操作時,避免獲取dom元素的布局信息,如果需要布局信息,最好在DOM操作之前就取好存放。
5.使用事件托管方式綁定事件
在DOM元素上綁定事件或影響頁面的性能,一方面,綁定事件本身會占用處理時間,另一方面,瀏覽器保存事件綁定也會占用內存。使用事件托管方式,即利用事件冒泡機制,只在父元素上綁定事件處理,用於處理所有子元素的事件,在事件處理函數中根據傳入的參數判斷事件源元素,針對不同的元素做不同的處理。這種方式有很大的靈活性,可以方便的添加或刪除子元素,不需要考慮因元素移除或添加需要修改事件綁定。