【譯】重繪與迴流no-no篇

觸發reflow(迴流)和repaint(重繪)

簡單回顧下回流和重繪的定義

  • 迴流:主要是計算位置和大小
  • 重繪:把內容畫(更新)到屏幕上

注意:迴流必定會觸發重繪,而重繪不必定會迴流javascript

渲染樹發生變化,就會產生迴流或重繪,例如:php

  • DOM節點的增刪改
  • 隱藏一個DOM節點,用display:none(迴流和重繪都會觸發),visibility: hidden(只有重繪,由於沒有幾何變化)
  • 在頁面上移動DOM節點
  • 增長或修改樣式
  • 改變瀏覽器大小,改變字體大小,甚至滾動頁面!

再看一些例子:css

var bstyle = document.body.style;
 
bstyle.padding = "20px"; // 迴流,重繪
bstyle.border = "10px solid red"; // 再一次迴流和重繪
 
bstyle.color = "blue"; // 只有重繪,沒有顏色變化
bstyle.backgroundColor = "#fad"; // 重繪
 
bstyle.fontSize = "2em"; // 迴流,重繪
 
// 新加元素 - 迴流,重繪
document.body.appendChild(document.createTextNode('dude!'));
複製代碼

還有些迴流會帶來更多的性能損耗,好比你把頁面頂部的一個div設置了動畫或者拉大了,致使頁面下面其餘部分都下去了。java

瀏覽器是聰明的

由於渲染樹的迴流和重繪比較損耗,瀏覽器目標在於減少負面影響。一個策略就是根本不作這件事情,至少如今不作。瀏覽器把你寫的更改放在一個隊列裏而後批量執行。用這種方法將會把屢次須要迴流的更改合成一次迴流進行計算。瀏覽器可以把屢次更改放進隊列,而後隔一段時間或者達到必定數量時一次性處理掉。git

可是有時候,腳本(js)可能會阻止瀏覽器這項優化措施,致使它(立刻)清理隊列以及執行全部更改。這件事發生在你修改樣式信息,好比:github

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. getComputedStyle(), 或者 currentStyle in IE
  5. 更多

以上這些主要是獲取某個節點的樣式信息的,一旦你調用這些,瀏覽器都須要給你最新的值。爲了這樣作,瀏覽器就須要執行全部計劃中的更改,清理隊列執行迴流。瀏覽器

舉個例子,快速連續地獲取和設置樣式(循環中),好比:app

// no-no!
el.style.left = el.offsetLeft + 10 + "px";
複製代碼

減小重繪和迴流

幾條建議以下:oop

  • 不要一行一行單獨地修改樣式。最好是直接修改class而不是修改樣式,這樣也比較可維護,不夠這個只對於靜態樣式而言。若是樣式是動態,那最好是用cssText去作。
// bad
var left = 10,
    top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";
 
// better 
el.className += " theclassname";
 
// 或者須要動態的修改top和left...
// better
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
複製代碼
  • 以「離線」方式批量處理DOM更改。離線意思是不在真實的DOM樹下(作更改),你能夠:性能

    • 使用documentFragment去臨時存一下更改
    • 克隆一個節點出來,而後在這個克隆的節點上作修改,而後在和原來的節點作替換
    • 先用display:none(須要一次迴流及重繪)隱藏掉元素,增長100次修改,而後再顯示(再花費一次迴流重繪)。這樣你就拿2次迴流和重繪換掉了100次。
  • 不要過度地使用computed styles。若是你須要使用,那就拿一次,而後保存到本地變量裏,並對這個本地變量進行操做。從新看下那個no-no例子:

// no-no!
for(big; loop; here) {
    el.style.left = el.offsetLeft + 10 + "px";
    el.style.top  = el.offsetTop  + 10 + "px";
}
 
// better
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";
}
複製代碼
  • 大致上,在你作了更改以後,須要考慮一下渲染樹裏有多少(節點)須要從新驗證。好比,使用絕對定位(absolute positioning)把一個節點做爲渲染樹中的一個主體節點,那麼給這個節點設置動畫的時候,不會影響太多別的節點,一些在(動畫涉及到的)區域內節點可能須要重繪,可是它們不須要回流。

原文

相關文章
相關標籤/搜索