天天看點

頁面重排和回流

重繪(replaint)——當頁面中的元素隻是外觀或風格被改變不影響布局,比如更換背景色background-color,這個過程就是重繪。

重排(relayout)——當RenderTree中的一部分(或全部)因為元素的規模尺寸,布局,隐藏等改變,浏覽器為了重新渲染部分或整個頁面,重新計算頁面元素位置和幾何結構的過程,也就是重新構造渲染樹 ,這個過程叫做重排(relayout)

回流(reflow)——Gecko中布局的稱謂,同時也是重排的别稱。

Reflow的成本比Repaint的成本高得多。DOM Tree裡的每個結點都會有reflow方法,一個結點的reflow很有可能導緻子結點,甚至父點以及同級結點的reflow。

1.觸發 reflow 因素

元素的布局和幾何屬性改變時就會觸發reflow。主要有這些屬性:

  • 頁面初始渲染
  • 添加/删除可見DOM元素
  • 改變元素位置 ----- 定位屬性及浮動 position,float
  • 改變元素尺寸(寬、高、内外邊距、邊框等) ----- 盒子模型相關屬性 height ,padding ,margin , display

    ,border-width ,min-height

  • 改變元素内容(文本或圖檔等) text-align , line-height ,vertival-align ,overflow ,

    font-size,font-family,font-weight

  • 改變視窗尺寸
  • 擷取元素的offsetWidth、offsetHeight、clientWidth、clientHeight、width、height、scrollTop、scrollHeight,請求了getComputedStyle(),

    或者 IE的 currentStyle

  1. 觸發 repaint 因素

    頁面中的元素更新外觀或風格相關的屬性時就會觸發重繪,如:background,color,visibility, border-style ,border-radius outline-color,cursor,text-decoration, box-shadow

    注意:由頁面的渲染過程可知,reflow必将會引起repaint,而repaint不一定會引起reflow

3.浏覽器對回流的優化

因為,回流花銷很大,是以大部分浏覽器對于回流都會進行優化,浏覽器會維護1個隊列,把所有會引起回流、重繪的操作放入這個隊列,等隊列中的操作到了一定的數量或者到了一定的時間間隔,浏覽器就會flush隊列,進行一個批處理。這樣就會讓多次的回流、重繪變成一次回流重繪。

雖然有了浏覽器的優化,但有些代碼可能會強制浏覽器提前flush隊列,這樣浏覽器的優化可能就起不到作用了。當你請求向浏覽器請求一些 style資訊的時候,就會讓浏覽器flush隊列,比如:

offsetTop, offsetLeft, offsetWidth, offsetHeight

scrollTop/Left/Width/Height

clientTop/Left/Width/Height

width,height

請求了getComputedStyle(), 或者 IE的 currentStyle

當你請求上面的一些屬性的時候,浏覽器為了給你最精确的值,需要flush隊列,因為隊列中可能會有影響到這些值的操作。即使你擷取元素的布局和樣式資訊跟最近發生或改變的布局資訊無關,浏覽器都會強行重新整理渲染隊列。

4.我們對回流的優化

減少回流、重繪其實就是需要減少對render tree的操作(合并多次DOM和樣式的修改),并減少對一些style資訊的請求,盡量利用好浏覽器的優化政策。具體方法有:

(1)直接改變className,或者使用cssText(會把原有的cssText清掉)

// bad
el.style.left = "10px";
el.style.top = "10px";

// good
el.className = " newClassName";
//or
el.style.cssText = "left:10px;top:10px;width:20px;height:20px;";

           

(2)對需要操作的元素進行”離線處理”,處理完後一起更新:

使用DocumentFragment進行緩存操作,引發一次回流和重繪;

使用display:none技術,隻引發兩次回流和重繪;

使用cloneNode(true or false) 和 replaceChild 技術,引發一次回流和重繪;

(3)不要經常通路會引起浏覽器flush隊列的屬性,如果你确實要通路,利用緩存

// bad
for (var i = 0; i < len; i++) {
  el.style.left = el.offsetLeft + x + "px";
  el.style.top = el.offsetTop + y + "px";
}
// good
var x = el.offsetLeft,
    y = el.offsetTop;
for (var i = 0; i < len; i++) {
  x += 10;
  y += 10;
  el.style = x + "px";
  el.style = y + "px"; }

           

(4)讓動畫元素脫離文檔流,減少回流的Render樹的規模(使用position屬性的fixed值或absolute值等等)。

(5)避免使用 table 布局。因為可能很小的一個小改動會造成整個 table 的重新布局。