頁面渲染過程
- 瀏覽器把HTML代碼解析成一顆
DOM Tree
,每個元素標籤都是DOM Tree
中的一個節點,根節點是Document
; - 在形成
DOM Tree
後,從DOM Tree
根節點開始,遍歷每一個可見的節點,並找到合適的、匹配的CSSOM
規則,應用在節點上; DOM Tree
和CSSOM Tree
連接在一起形成Render Tree
;- 最終繪製出頁面,顯示在屏幕上
什麼叫回流與重繪?
- 頁面迴流:當
render tree
中的一部分(或全部)因爲元素的規模尺寸、佈局、隱藏等改變而引起的頁面重新渲染(或者叫作重新構建繪製); - 頁面重繪:當
render tree
中的一些元素需要更新屬性,但這些屬性只會影響元素的外觀、風格,而不會影響元素的佈局,此類的頁面渲染叫作頁面重繪。
在網頁生成的時候,至少會渲染一次。用戶訪問的過程中,任何對Render Tree
節點的操作都會引起重新渲染。
需要注意的是頁面重繪不一定會引起迴流,但迴流一定會引起頁面重繪。
常見的迴流與重繪操作
DOM
節點的添加、刪除;- 調整窗口大小(resize事件);
- 元素位置改變、元素尺寸改變(
width/height/padding/border/margin
); CSS
僞類的激活(如hover
);- 對樣式的操作(不同屬性有不同的影響)
- 元素樣式屬性的讀取操作
開發過程中,提高性能的技巧
頁面的迴流、重繪在項目中是不可避免的,但過高、頻繁的迴流、重繪必定導致網頁性能低下,影響客戶體驗感。所以,在開發過程中,我們應儘可能的避免迴流、重繪,儘量少觸發頁面的重新渲染。
1. 儘量不要把讀操作和寫操作,放在一個語句裏面。
// 下列操作只會造成一次迴流、重繪
div.style.color = 'blue';
div.style.marginTop = '30px';
// 下列操作造成兩次的迴流、重繪
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
在樣式的寫操作之後,有對元素進行
offset
、scroll
、client
、getComputedStyle()
屬性讀取操作,都會觸發瀏覽器立即重新渲染。
// 壞的寫法 --> 讀寫操作放同一個語句
div.style.left = div.offsetLeft + 10 + "px";
// 好的寫法
var left = div.offsetLeft;
div.style.left = left + 10 + 'px';
2. 不要一條條地修改樣式,而是通過改變class
、cssText
屬性,一次性修改樣式。
// 壞的寫法
div.style.left = '10px';
div.style.top = '10px';
// 好的寫法
div.className += 'newClassName';
div.style.cssText += 'background-color:red;font-size:20px;';
3. 儘可能使用Document Fragment
對象或者cloneNode()
,在克隆的節點上進行操作,然後再用克隆的節點替換原始節點。
// 壞的寫法
for( var i = 0; i<2; i++ ){
var li = document.createElement('li');
li.textContent = i;
ul.appendChild(li);
}
// 好的寫法
var fragment = document.createDocumentFragment();
for( var i = 0; i<2; i++ ){
var li = document.createElement('li');
li.textContent = i;
fragment.appendChild(li);
}
ul.appendChild(fragment);
4. 先將元素設置爲display:none
,再對元素進行操作,最後恢復顯示,這樣只需要兩次的重新渲染。
5. 儘可能地使用position;
,而不是float
來進行元素浮動。因爲使用position
屬性不用考慮它對其他元素的影響,迴流的開銷會比較小。
6. 儘可能地將display
屬性設爲可見,不可見屬性的元素不會影響迴流和重繪 (visibility
的元素只對重繪有影響,不影響迴流)。
7. 使用虛擬DOM
的腳本庫,比如React
、Vue
等。
8. 使用window.requestAnimationFrame()
、window.requestIdleCallback()
這兩個方法調節重新渲染。
注意:window.requestAnimationFrame()
只能在IE10及以上使用,而window.requestIdleCallback()
在IE上是不支持的。這兩個方法在阮一峯阮一峯的博客上有詳細的介紹。
參考鏈接
- HTML5佈局之路(著:劉國利)
- 網頁性能管理詳解(著:阮一峯)