本文參考: 《高性能JS》、網頁性能管理詳解javascript
瀏覽器下載完頁面中的全部組件--HTML標記、JS、CSS、圖片--以後會解析並生成兩個內部數據結構:css
"生成佈局"(flow)和"繪製"(paint)這兩步,合稱爲"渲染"(render)。html
網頁生成的時候,至少會渲染一次。用戶訪問的過程當中,還會不斷從新渲染。java
DOM 樹種的每個須要顯示的節點在渲染樹中至少存在一個對應的節點(隱藏的DOM元素在渲染樹中沒有對應的節點)。web
渲染樹中的節點稱爲「幀(frames)」或「盒(boxes)」,符合CSS模型的定義。chrome
重排是什麼:從新生成佈局。當DOM 的變化影響了元素的幾何屬性(寬和高)--好比改變邊框寬度或給段落增長文字致使行數增長--瀏覽器須要從新計算元素的幾何屬性,一樣其餘元素的幾何屬性和位置也會所以受到影響。瀏覽器會使渲染樹中受到影響的部分失效,並從新構造渲染樹。這個過程稱爲重排。瀏覽器
重繪是什麼:從新繪製。完成重排後,瀏覽器會從新繪製受影響的部分到屏幕中。這個過程稱爲重繪。數據結構
重排必定會致使重繪,重繪不必定致使重排。若是DOM變化不影響幾何屬性,元素的佈局沒有改變,則只發生一次重繪(不須要重排)。app
當頁面佈局和幾何屬性改變時發生「重排」。以下:函數
整個頁面或局部。例如:當滾動條出現時觸發整個頁面的重排。
重排和重繪會不斷觸發,這是不可避免的。可是,它們很是耗費資源,是致使網頁性能低下的根本緣由。
提升網頁性能,就是要下降"重排"和"重繪"的頻率和成本,儘可能少觸發從新渲染。
前面提到,DOM變更和樣式變更,都會觸發從新渲染。可是,瀏覽器已經很智能了,會盡可能把全部的變更集中在一塊兒,排成一個隊列,而後一次性執行,儘可能避免屢次從新渲染。
div.style.color = 'blue';
div.style.marginTop = '30px';
複製代碼
上面代碼中,div元素有兩個樣式變更,可是瀏覽器只會觸發一次重排和重繪。
若是寫得很差,就會觸發兩次重排和重繪。
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
複製代碼
上面代碼對div元素設置背景色之後,第二行要求瀏覽器給出該元素的位置,因此瀏覽器不得不當即重排。
獲取佈局信息的操做會致使列隊刷新,如下屬性和方法須要返回最新的佈局信息,最好避免使用。
offsetTop
, offsetLeft
, offsetWidth
, offsetHeight
scrollTop
, scrollLeft
, scrollWidth
, scrollHeight
clientTop
, clientLeft
, clientWidth
, clientHeight
getComputedStyle()
(currentStyle in IE)clientTop:元素上邊框的厚度,當沒有指定邊框厚底時,通常爲0。
scrollTop:位於對象最頂端和窗口中可見內容的最頂端之間的距離,簡單地說就是滾動後被隱藏的高度。
offsetTop:獲取對象相對於由offsetParent屬性指定的父座標(css定位的元素或body元素)距離頂端的高度。
clientHeight:內容可視區域的高度,也就是說頁面瀏覽器中能夠看到內容的這個區域的高度,通常是最後一個工具條如下到狀態欄以上的這個區域,與頁面內容無關。
scrollHeight:IE、Opera 認爲 scrollHeight 是網頁內容實際高度,能夠小於 clientHeight。FF 認爲 scrollHeight 是網頁內容高度,不過最小值是 clientHeight。
offsetHeight:獲取對象相對於由offsetParent屬性指定的父座標(css定位的元素或body元素)的高度。IE、Opera 認爲 offsetHeight = clientHeight + 滾動條 + 邊框。FF 認爲 offsetHeight 是網頁內容實際高度,能夠小於clientHeight。offsetHeight在新版本的FF和IE中是同樣的,表示網頁的高度,與滾動條無關,chrome中不包括滾動條。
Window.getComputedStyle()方法返回一個對象,該對象在應用活動樣式表並解析這些值可能包含的任何基本計算後報告元素的全部CSS屬性的值。 私有的CSS屬性值能夠經過對象提供的API或經過簡單地使用CSS屬性名稱進行索引來訪問。
因此,從性能角度考慮,儘可能不要把讀操做和寫操做,放在一個語句裏面。
// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";
// good
var left = div.offsetLeft;
var top = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";
複製代碼
通常的規則是:
當查詢佈局信息時,好比獲取偏移量(offset)、滾動位置(scroll)或計算出的樣式值(computedstyle values)時,瀏覽器爲了返回最新值,會刷新隊列並應用全部變動。不利於優化。
因此應該儘可能減小布局信息的獲取次數,獲取後把它賦值給局部變量,而後再操做局部變量
// 優化前
myElement.style.left = 1 + myElement.offsetLeft + 'px';
myElement.style.top = 1 + myElement.offsetTop + 'px';
if (myElement.offsetLeft >= 500) {
stopAnimation();
}
// 優化後
// 獲取一次起始位置的值,而後賦值給一個變量,在動畫循環中直接使用變量再也不查詢偏移量
var current = myElement.offsetLeft;
current++;
myElement.style.left = current + 'px';
myElement.style.top = current + 'px';
if (myElement.offsetLeft >= 500) {
stopAnimation();
}
複製代碼
cssText
屬性如今大部分瀏覽器都自動優化了
// 優化前
var el = document.getElementById('mydiv');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';
// 優化後
var el = document.getElementById('mydiv');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
複製代碼
class
名稱而不是修改內聯樣式var el = document.getElementById('mydiv');
el.className = "active";
複製代碼
var ul = document.getElementById('mylist');
ul.style.display = 'none';
appendDataToElement(ul, data); // 更新指定節點數據的函數
ul.style.display = 'block';
複製代碼
文檔片斷是個輕量級的document對象,用於更新和移動節點。當你附加一個片斷到節點中,實際上添加的是該片斷的子節點,而不是片斷自己。
該方法產生的DOM遍歷和重排次數最少。
//建立一個文檔片斷
var fragment = document.createDocumentFragment();
// 更新文檔片斷的數據
appendDataToElement(fragment, data);
// 將文檔片斷附加到原始列表中(實際添加的是子節點)
document.getElementById('mylist').appendChild(fragment);
複製代碼
實例以下:
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>使用fragment進行重排重繪</title> </head> <body> <ul id="myList"> <li>a</li> <li>b</li> </ul> <p> 向上面的ul中加入兩個新的li,比較使用fragment和不使用的性能 </p> <script> console.time(0); var newLi1 = document.createElement('li'); newLi1.innerHTML = 'c'; var newLi2 = document.createElement('li'); newLi2.innerHTML = 'd'; document.getElementById('myList').appendChild(newLi1); document.getElementById('myList').appendChild(newLi2); console.timeEnd(0) console.time(1); var fragment = document.createDocumentFragment(); var newLi1 = document.createElement('li'); newLi1.innerHTML = 'c'; var newLi2 = document.createElement('li'); newLi2.innerHTML = 'd'; fragment.appendChild(newLi1); fragment.appendChild(newLi2); document.getElementById('myList').appendChild(fragment); console.timeEnd(1) </script> </body> </html> 複製代碼
var old = document.getElementById('mylist');
// 對舊節點備份
var clone = old.cloneNode(true);
appendDataToElement(clone, data);
// 用副本節點代替舊節點
old.parentNode.replaceChild(clone, old);
複製代碼
許多展開區域的幾何動畫會將頁面其餘部分推向下方。通常來講,重排隻影響渲染樹中的一部分,可是也可能影響很大的部分。
當頁面頂部的一個動畫推移頁面整個餘下的部分時,會致使一次代價昂貴的大規模重排。
使用如下步驟能夠避免頁面中的大部分重排: