原文連接:http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/javascript
轉載連接:http://www.cnblogs.com/ihardcoder/p/3927709.htmlphp
我的備註:css
restyle——沒有幾何結構改變的渲染樹變化(而回流reflow會同時影響佈局layout)。html
改變任何影響構造渲染樹的行爲都會觸發重繪repaint,例如java
如何減小重繪repaint和迴流reflownode
有沒有被標題中的5個「R」嚇到?今天,咱們來討論一下瀏覽器的渲染(Rendering)-一個產生於Page 2.0生命週期中,甚至有時候會在下載瀑布流中出現的過程。web
咱們來討論瀏覽器在接收到HTML、CSS和JavasSript後,如何把你的頁面呈如今屏幕上。瀏覽器
不一樣的瀏覽器的渲染過程存在些許不一樣,但大致的機制是同樣的,下圖展現的是瀏覽器自下載徹底部的代碼後的大體流程緩存
首先咱們先看一個例子:app
<html> <head> <title>Beautiful page</title> </head> <body> <p> Once upon a time there was a looong paragraph... </p> <div style="display: none"> Secret message </div> <div><img src="..." /></div> ... </body> </html>
HTML結構中的每一個標籤和標籤間的文字都會被映射爲DOM樹種的一個節點(實際上,空白區域也會被映射爲一個text節點,爲了簡單說明,在此忽略),構建完成的DOM樹結構以下:
documentElement (html) head title body p [text node] div [text node] div img ...
因爲渲染樹會忽略head內容和隱藏的節點,而且會將<p>中的多行文字按行數映射爲單獨的渲染節點,故構建完成的渲染樹結構以下:
root (RenderView) body p line 1 line 2 line 3 ... div img ...
渲染樹的根節點是一個包括全部其餘節點的結構體(盒子)。你能夠將它理解爲瀏覽器窗口的內部區域(我的理解爲可繪製區域,即不包括瀏覽器邊框、菜單欄、標籤欄等等),頁面被限制在此區域內。嚴格來講,webkit將渲染樹的根節點稱爲渲染視圖-RenderView,渲染視圖符合CSS初始包含塊-initial containing block,也就是瀏覽器的整個可繪製區域,從座標(0,0)到(window.innerWidth,window.innerHeight)。
接下來,咱們將研究瀏覽器是如何經過循環遍歷渲染樹把頁面展現到屏幕上的。
同一時間內至少存在一個頁面初始化layout行爲和一個繪製行爲(除非你的頁面是空白頁-blank)。在此以後,改變任何影響構造渲染樹的行爲都會觸發如下一種或者多種動做:
重繪和迴流的性能消耗是很是嚴重的,破壞用戶體驗,形成UI卡頓。
改變任何影響構造渲染樹的行爲都會觸發重繪,例如
舉個栗子:
var bstyle = document.body.style; // 緩存 bstyle.padding = "20px"; // 觸發重繪和迴流 bstyle.border = "10px solid red"; // 再次觸發重繪和迴流 bstyle.color = "blue"; // 只觸發重繪,由於幾何結構沒有改變 bstyle.backgroundColor = "#fad"; // 同上 bstyle.fontSize = "2em"; // 再再次觸發重繪和迴流 // 新增DOM節點,再再再次觸發重繪和迴流 document.body.appendChild(document.createTextNode('dude!'));
有些迴流行爲要比其餘的花銷大一些。設想以下情景,一個直屬於body節點的渲染樹,若是你在此渲染樹中亂搞,它不會影響不少其餘節點(這個長句翻譯很差,原文以下:Think of the render tree - if you fiddle with a node way down the tree that is a direct descendant of the body, then you're probably not invalidating a lot of other nodes)。可是若是將頁面頂部的一個div作動畫或改變尺寸,頁面的其餘部分會被擠來擠去,這聽起來會消耗不少性能。
瀏覽器一直在努力減小消耗巨大的重繪和迴流行爲。要麼選擇不執行,要麼至少不當即執行。瀏覽器會生成一個隊列用於緩存這些行爲而且以塊爲單位執行它們。經過這種方法,屢次引起重繪或迴流的操做會被組合在一塊兒,以便在一個迴流中完成。瀏覽器將這些操做加入到緩存隊列中,當到達必定的時間間隔,或者累積了足夠多的操做行爲後執行它們。
可是,有時候某些的代碼會破壞上述的瀏覽器優化機制,致使瀏覽器刷新緩存隊列而且執行全部已已緩存的操做行爲。這種狀況發生在請求/獲取下面這些樣式的行爲中:
以上的行爲本質上是獲取一個節點的樣式信息,瀏覽器必須提供最新的值。爲了達到此目的,瀏覽器須要將緩存隊列中的全部行爲所有執行完畢,而且被強制迴流。
因此,在一條邏輯中同時執行set和get樣式操做時很是很差的,以下:
el.style.left = el.offsetLeft + 10 + "px";
減小由於重繪和迴流引發的糟糕用戶體驗的本質是儘可能減小重繪和迴流,減小樣式信息的set行爲。能夠經過如下幾點來優化:
// 糟糕的辦法 var left = 10, top = 10; el.style.left = left + "px"; el.style.top = top + "px"; //靜態樣式經過改變classname // better el.className += " theclassname"; // 動態樣式統一修改cssText // better el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
//糟糕的作法 for(big; loop; here) { el.style.left = el.offsetLeft + 10 + "px"; el.style.top = el.offsetTop + 10 + "px"; } //優化後的代碼 var left = el.offsetLeft, top = el.offsetTop esty = el.style; for(big; loop; here) { left += 10; top += 10; esty.left = left + "px"; esty.top = top + "px"; }
(廢話就不翻譯了,大概就是一些吐槽IE開發者工具的話)
如今(原文做於2009年12月)有不少能夠幫助咱們深刻了解瀏覽器重繪和迴流機制的工具。
Douglas Crockford去年提到,咱們可能會對一些不太瞭解的CSS作一些愚蠢的事情,而且我被包括在內。我被引入了一個項目組,研究一種奇怪的現象:在IE6瀏覽器中增大font-size會引發CPU佔用率到達100%,而且會持續10到15分鐘,IE瀏覽器纔會完成重繪行爲。
有了工具的輔助,咱們沒有任何理由再作一些愚蠢的CSS操做了。
順便提一句,若是有一種像Firebug的工具能夠象查看DOM結構同樣查看渲染樹,是否是很cooooooooooooooool?
下面咱們簡單的看一個如何運用工具來證實restyle(沒有幾何結構改變的渲染樹變化)和迴流(同時影響佈局layout)、重繪。
第一個測試,咱們比較解決同一問題的兩種方法。第一種方法,改變一些樣式,在每次改變以後檢查一次唄改變的樣式。
bodystyle.color = 'red'; tmp = computed.backgroundColor; bodystyle.color = 'white'; tmp = computed.backgroundImage; bodystyle.color = 'green'; tmp = computed.backgroundAttachment;
第二種方法,在等待所有樣式改變完畢後再檢查變化的樣式信息。
bodystyle.color = 'yellow'; bodystyle.color = 'pink'; bodystyle.color = 'blue'; tmp = computed.backgroundColor; tmp = computed.backgroundImage; tmp = computed.backgroundAttachment;
上面兩種方法用到的幾個變量以下:
var bodystyle = document.body.style; var computed; if (document.body.currentStyle) { computed = document.body.currentStyle; } else { computed = document.defaultView.getComputedStyle(document.body, ''); }
上面兩中方法的樣式改變經過click事件觸發。測試頁面-restyle.html(點擊「dude」)。咱們將第一個測試稱爲restyle測試。
第二個測試在第一個測試的基礎上,同事改變影響佈局的樣式。
// 每次修改後都檢查 bodystyle.color = 'red'; bodystyle.padding = '1px'; tmp = computed.backgroundColor; bodystyle.color = 'white'; bodystyle.padding = '2px'; tmp = computed.backgroundImage; bodystyle.color = 'green'; bodystyle.padding = '3px'; tmp = computed.backgroundAttachment; // 所有修改完畢後再檢查 bodystyle.color = 'yellow'; bodystyle.padding = '4px'; bodystyle.color = 'pink'; bodystyle.padding = '5px'; bodystyle.color = 'blue'; bodystyle.padding = '6px'; tmp = computed.backgroundColor; tmp = computed.backgroundImage; tmp = computed.backgroundAttachment;
咱們稱第二個測試爲relayout測試,測試頁面請點擊。
咱們經過DynaTrace工具獲得restyle測試的表現以下圖:
等頁面加載完畢後,在第2秒左右點擊觸發第一種方案(即每次修改樣式後當即檢查),而後在第4秒左右再次點擊觸發第二種方案(即等待全部樣式修改完畢後再統一檢查)。
DynaTrace工具會顯示頁面的加載過程,從上圖能夠看到IE的logo圖標被加載的時間節點。把鼠標移至Rendering一行以便追蹤點擊事件,滑動滾輪放大想要追蹤的區域能夠查看詳細信息,以下圖:
從上圖中能夠清晰的看到表明JavaScript行爲的藍色柱形條,一屆表明渲染行爲的綠色柱形條。經過這個簡單的實驗,咱們能夠注意到兩個柱形條的長度,也就是比較渲染行爲比JavaScript行爲多花費的時間。在Ajax以及富應用中,性能瓶頸並非JavaScript行爲,而是DOM節點的操做使用和渲染行爲。
接下來咱們來運行relayout測試,也就是涉及幾何結構改變的操做行爲。經過測試工具的「PurePaths」視圖,查看每種行爲執行時間的瀑布流。下圖中高亮部分顯示的是第一次點擊事件,執行一段JavaScript邏輯實現一些layout操做。
以下圖所示,咱們能夠看到在此次的測試中,除了與第一次測試一樣的具備表明「繪圖」的綠色柱形條之外,還有一個新增的區域-「計算佈局流」,由於此次測試中同時觸發了重繪和迴流。
接下來,咱們經過SpeedTracer工具在Chrome下運行上面兩個測試。
第一個測試-restyle測試的運行結果以下圖所示:
總的來講,仍然是一次點擊觸發一次重繪,可是咱們注意到,在第一次點擊的時候,會有50%的時間消耗在計算樣式(Style Recalculation)上。致使這種結果的緣由是咱們在每次改變樣式後都檢查了一次樣式信息。
展開事件詳細信息後能夠清晰的看到,在第一次點擊事件後,樣式被計算了3次。而第二次點擊值計算了一次。以下圖所示:
接下來運行第二個測試-relayout測試。整體事件信息與restyle測試大體相同:
可是詳情頁顯示的信息能夠看到第一次點擊後觸發了3次迴流(由請求樣式信息操做觸發),第二次點擊只觸發了一次迴流。經過本工具能夠清晰的看到瀏覽器內部到底發生了什麼。
上述兩種工具的區別在於:DynaTrace會顯示layout行爲被執行和加入執行隊列的詳細時間,而SpeedTracer不會;SpeedTracer會將restyle與reflow/layout兩種瀏覽器行爲區別開,而DynaTrace不會。難道IE瀏覽器自己不會區分這兩種行爲?另外,在兩種不一樣的邏輯測試-改變-最後檢查(change-end-touch)與改變-當即檢查(change-then-touch)中,DynaTrace並不會顯示二者觸發迴流的次數不一樣(第一種之觸發一次,第二次觸發3次,而DynaTrace統一顯示爲一次),難道IE瀏覽器的工做機制本就如此?
即便運行上述測試幾百次,IE瀏覽器仍然不關心你在改變樣式後是否請求樣式信息。(譯者注:我彷佛感到原文做者對IE滿滿的惡意...)
在屢次運行上述測試後,獲得幾點結論以下:
在全部瀏覽器(IE系列不在「全部」的範疇)的測試結果顯示,只修改樣式的時間花銷僅僅是同時改變樣式和觸發layout的一半(我本該對比只改變樣式和只改變layout的時間的,可是我沒有,不用謝)。順便提一下IE6,它的layout時間花銷是隻改變樣式的4倍。(呵呵)
很是感謝各位對這篇文章的支持。但願各位能經過運動上文提到的測試工具改善工做,而且時刻注意迴流的觸發操做。最後,咱們複習一下幾個術語:
擴展閱讀,前三篇對瀏覽器內部機制研究比較深刻,推薦: