迴流與重繪

原文地址:http://www.stubbornella.org/content/2009/03/27/reflows-repaints-css-performance-making-your-javascript-slow/
原作者:Nicole Sullivan
翻譯:張鑫旭

關於迴流(reflows)與重繪(repaints),我已經在twitterdelicious上發佈,但是並沒有在演講中提到或是以文章形式發佈。

第一次讓我開始思考關於迴流(reflows)與重繪(repaints)的問題是在和ParisWeb上的Mr. Glazman做一個firey交換的時候。我可能有一些頑固,但是我確實聽了他的一些理論。:)Stoyan和我開始討論如何量化這個問題。

展望性能社區,除了一些典型的黑盒實驗外,需要與瀏覽器廠商有更多的合作。對於性能,瀏覽器製造者知道哪些是重要的,哪些是不相干的。Opera列出“reflow和repaint是減緩JavaScript的三大主要原因之一”一文,所以其肯定值得一看。// zxx: Firefox瀏覽器相關內容可以看這裏;Safari可以看這裏

讓我們從一些背景資料開始,當一個元素的外觀的可見性visibility發生改變的時候,重繪(repaint)也隨之發生,但是不影響佈局。類似的例子包括:outline, visibility, or background color。根據Opera瀏覽器,重繪的代價是高昂的,因爲瀏覽器必須驗證DOM樹上其他節點元素的可見性。而回流更是性能的關鍵因爲其變化涉及到部分頁面(或是整個頁面)的佈局。一個元素的迴流導致了其所有子元素以及DOM中緊隨其後的祖先元素的隨後的迴流。

例如:

<body>
<div class="error">
	<h4>我的組件</h4>
	<p><strong>錯誤:</strong>錯誤的描述…</p>
	<h5>錯誤糾正</h5>
	<ol>
		<li>第一步</li>
		<li>第二步</li>
	</ol>
</div>
</body>

在上面的HTML片段中,對該段落(<p>標籤)迴流將會引發強烈的迴流,因爲它是一個子節點。這也導致了祖先的迴流(div.error和body – 視瀏覽器而定)。此外,h5和ol也會有簡單的迴流,因爲其在DOM中在迴流元素之後。就Opera而言,大部分的迴流將導致頁面的重新渲染。

Opera原文:Reflows are very expensive in terms of performance, and is one of the main causes of slow DOM scripts, especially on devices with low processing power, such as phones. In many cases, they are equivalent to laying out the entire page again.

既然它們對性能影響如此可怕,那什麼會導致迴流呢?

不幸的是,很多的東西,其中一些還與CSS的書寫特別相關。

  1. 調整窗口大小(Resizing the window)
  2. 改變字體(Changing the font)
  3. 增加或者移除樣式表(Adding or removing a stylesheet)
  4. 內容變化,比如用戶在input框中輸入文字(Content changes, such as a user typing text in
    an input box)
  5. 激活 CSS 僞類,比如 :hover (IE 中爲兄弟結點僞類的激活)(Activation of CSS pseudo classes such as :hover (in IE the activation of the pseudo class of a sibling))
  6. 操作 class 屬性(Manipulating the class attribute)
  7. 腳本操作 DOM(A script manipulating the DOM)
  8. 計算 offsetWidth 和 offsetHeight 屬性(Calculating offsetWidth and offsetHeight)
  9. 設置 style 屬性的值 (Setting a property of the style attribute)

Mozilla關於迴流的文章羅列了導致迴流的要點以及何時可以減少他們。

如何避免迴流或將它們對性能的影響降到最低?

注意:這裏我限定了自己只能討論CSS對迴流的影響,如果您是一位JavaScript程序員,我是推薦您讀一下我的reflow鏈接(zxx: 原作者收藏標記的一些關於reflow的一些文章或頁面鏈接),有一些非常好的東西,沒有直接關係到CSS。

  1. 如果想設定元素的樣式,通過改變元素的 class 名 (儘可能在 DOM 樹的最末端)(Change classes on the element you wish to style (as low in the dom tree as possible))
  2. 避免設置多項內聯樣式(Avoid setting multiple inline styles)
  3. 應用元素的動畫,使用 position 屬性的 fixed 值或 absolute 值(Apply animations to elements that are position fixed or absolute)
  4. 權衡平滑和速度(Trade smoothness for speed)
  5. 避免使用table佈局(Avoid tables for layout)
  6. 避免使用CSS的JavaScript表達式 (僅 IE 瀏覽器)(Avoid JavaScript expressions in the CSS (IE only))

儘可能在DOM樹的最末端改變class

迴流可以自上而下,或自下而上的迴流的信息傳遞給周圍的節點。迴流是不可避免的,但可以減少其影響。儘可能在DOM樹的裏面改變class,可以限制了迴流的範圍,使其影響儘可能少的節點。例如,你應該避免通過改變對包裝元素類去影響子節點的顯示。面向對象的CSS始終嘗試獲得它們影響的類對象(DOM節點或節點),但在這種情況下,它已儘可能的減少了迴流的影響,增加性能優勢。

避免設置多層內聯樣式

我們都知道與DOM交互很慢。我們嘗試在一種無形的DOM樹片段組進行更改,然後整個改變應用到DOM上時僅導致了一個迴流。同樣,通過style屬性設置樣式導致迴流。避免設置多級內聯樣式,因爲每個都會造成迴流,樣式應該合併在一個外部類,這樣當該元素的class屬性可被操控時僅會產生一個reflow。

動畫效果應用到position屬性爲absolute或fixed的元素上

動畫效果應用到position屬性爲absolute或fixed的元素上,它們不影響其他元素的佈局,所它他們只會導致重新繪製,而不是一個完整迴流。這樣消耗會更低。

犧牲平滑度換取速度

Opera還建議我們犧牲平滑度換取速度,其意思是指您可能想每次1像素移動一個動畫,但是如果此動畫及隨後的迴流使用了100%的CPU,動畫就會看上去是跳動的,因爲瀏覽器正在與更新迴流做鬥爭。動畫元素每次移動3像素可能在非常快的機器上看起來平滑度低了,但它不會導致CPU在較慢的機器和移動設備中抖動。

避免使用table佈局

避免使用table佈局。可能您需要其它些避免使用table的理由,在佈局完全建立之前,table經常需要多個關口,因爲table是個和罕見的可以影響在它們之前已經進入的DOM元素的顯示的元素。想象一下,因爲表格最後一個單元格的內容過寬而導致縱列大小完全改變。這就是爲什麼所有的瀏覽器都逐步地不支持table表格的渲染(感謝Bill Scott提供)。然而有另外一個原因爲什麼表格佈局時很糟糕的主意,根據Mozilla,即使一些小的變化將導致表格(table)中的所有其他節點回流。

Jenny Donnelly, YUI 數據表格 widget的所有者,建議使用數據表格的固定佈局以便更有效的佈局算法,任何表格-佈局的值除了”auto”將引發一個固定佈局,根據CSS2.1規範,這將允許表格一行一行的呈遞。Quirksmode顯示,大部分的瀏覽器對錶格佈局屬性支持良好。

In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.

固定佈局, CSS 2.1 規範

This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.

自動佈局, CSS 2.1 規範

避免使用CSS的JavaScript表達式

這項規則較過時,但確實是個好的主意。主要的原因,這些表現是如此昂貴,是因爲他們每次重新計算文檔,或部分文檔、迴流。正如我們從所有的很多事情看到的:引發迴流,它可以每秒產生成千上萬次。當心!

進一步的學習

Yahoo!出色的性能團隊做了一個實驗,以確定最佳的方法引入外部的樣式表文件。我們建議把鏈接標記放在頭部,儘管其比所有其他阻礙進一步渲染的方法慢一秒(6.3至7.3秒)。雖然逐步地渲染不可改變的(用戶討厭盯着一個空白屏幕),但它使我對下載組件和整體響應時間的渲染,重畫,迴流和CPU的顛簸造成的影響感到好奇。如果我們可以減少加載過程中的迴流個數我們可以奪回了失去的時間的十分之一(100毫秒)嗎?如果它是多達一半呢?

在SXSW我試圖說服史蒂夫的迴流是很重要的。我告訴他一個我已經做了很長一段時間的實驗,只是沒有時間。我希望有人能在我留下來的那個地方繼續(提示!提示!)。雖然加載頁面我想故意引發不同程度的迴流。這或許可以通過切換一個body(實驗)標籤的class名稱來改變body的最後一個child(沒有子節點)完成。比較這兩者情況,並增加每秒迴流的數目,我們可以關聯迴流反應時間。衡量JS反應對迴流的影響將會更難,因爲我們所做能夠引發迴流的事情都可能會影響實驗。

最後,量化影響的趣味只有那麼一點點,因爲瀏覽器廠商會告訴我們它的問題。或許更有趣的是把注意力集中在迴流的原因以及如何避免它們。這將需要更好的工具,所以我需要瀏覽器廠商和性能社區共同努力,使之成爲現實!

事實說話

也許你是一個視覺關注者,這些視頻通過很酷的方式將回流過程可視化展示出來。//zxx: youtube視頻,想看,需翻牆。

  1. http://www.youtube.com/watch?v=nJtBUHyNBxs
  2. http://www.youtube.com/watch?v=ZTnIxIA5KGw
  3. http://www.youtube.com/watch?v=dndeRnzkJDU

迴流消失的瘋狂

爲了改進性能,瀏覽器廠商可能限制迴流影響臨近的節點或者聯合其他幾個迴流形成一次大的改變,就如Mozilla的這篇文章中展示的。這可以提高性能,但有時也可能導致顯示問題。您可以使用我們所瞭解到的關於迴流的知識,在必要糾正相關顯示問題的時候觸發它們。

例如,我們改變圖片優化網站上的選項卡,http://smush.it,隨着選項卡的不同,內容區域的高度是不斷變化的,有時候,下面的投影會被留下,這是因爲內容區域上的父節點被切換而其容器可能沒有迴流,下面這張圖是模擬的,因爲這個bug很難用相機捕捉,任何試圖獲取操作都會導致迴流而糾正顯示。如果你自己發現類似的bug,移動背景圖片到下面切換的DOM元素上。

模擬圖 >> 張鑫旭-鑫空間-鑫生活

另外一個例子是動態添加列表項,當您將列表項從9個增加到10個或是從99個增加到100個,列表的數字將不會正確的排隊顯示,這在所有瀏覽器都是。當總數按數量的順序增加且瀏覽器不迴流兄弟節點時,隊列被打破。快速切換整個列表的顯示或是增加添加一個class類,即使沒有相關聯的樣式,也會導致迴流和正確的隊列顯示。

工具

最近有小部分工具產生了一些波瀾,Stoyan Stefanov和我已經一直在尋找好的方法來測量回流與重繪,這裏有一些(儘管比較早)。要當心,有的在我正確使用之前把瀏覽器給搞跛了。大部分情況下,你需要每晚安裝下最新的版本。

當Mozilla公開MozAfterPaint Firefox API時,互聯網界議論紛紛。

  • 更新:Google的Lindsey Simon寫了個可以在任意瀏覽器下測試迴流時間的書籤工具。非常的贊,注意:所有的震動都是正常的。//zxx: 我測試此鏈接爲500錯誤
  • John Resig寫了個書籤工具來可視化繪製事件。
  • Kyle Scholz創建了一個工具用來在頁面加載之前可視化繪製事件
  • Yoono的Alex創建了XUL分析工具

是否其他人見到過好的關於迴流評估的工具?請發送告知我。

還有其他一對不是直接用來處理迴流的工具。

最後,我們需要一個跨瀏覽器的工具來量化並減少迴流和重繪。我希望性能社區能夠與瀏覽器廠商合作,使這一工具成爲現實。瀏覽器廠商已經告訴我們有一段時間了,這是我們未來需要看到的,希望在我們手中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章