前端性能-迴流&重繪

迴流&重繪

先了解瀏覽器渲染過程

參考自 (MDN).javascript

瀏覽器渲染大體流程:

  1. 處理 HTML 標記並構造 DOM 樹。
  2. 處理 CSS 標記並構造 CSSOM 樹。
  3. 將 DOM 與 CSSOM 合併成一個渲染樹。
  4. 根據渲染樹來佈局,以計算每一個節點的幾何信息。
  5. 將各個幾點繪製到屏幕上。

生成渲染樹:

構建渲染樹,瀏覽器須要作一下工做:java

  1. 從 DOM 樹的根節點開始遍歷每一個 可見 節點。
  2. 對於每一個可見的節點,找到 CSSOM 樹中對應的規則,並應用它們。
  3. 根據每一個可見節點以及其對應的樣式,組合生成渲染樹。

第一步遍歷節點的時候,須要知道什麼節點是不可見的。web

  • 一些不會渲染輸出的節點。好比 script、meta、link等。
  • 某些節點經過 CSS 隱藏。注意 visibility: hidden 與 display: none 是不同的。前者隱藏元素,但元素仍佔據着佈局空間(即將其渲染成一個空框),然後者 (display: none) 將元素從渲染樹中徹底移除,元素既不可見,也不是佈局的組成部分。

注意: 渲染樹只包含可見的節點瀏覽器

迴流(reflow)

Render Tree 中部分或所有, 因元素的尺寸、佈局、隱藏等改變而須要從新構建,瀏覽器從新渲染的過程稱爲 迴流。緩存

會致使迴流的操做:函數

  • 頁面首次渲染。
  • 瀏覽器窗口大小發生改變。
  • 元素尺寸或者位置發生改變。
  • 元素內容變化(文字數量或者圖片大小發生改變)。
  • 元素字體大小的改變。
  • 添加或者刪除可見DOM 元素。
  • 激活 CSS 僞類 (eg: :hover)。
  • 查詢某些屬性或調用某些方法。

一些經常使用且會致使迴流的屬性和方法。佈局

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

重繪(repaint)

當頁面中元素樣式的改變並不影響b佈局時(eg:colorbackground-color等),瀏覽器會將新樣式賦予給元素並從新繪製它,這個過程稱爲重繪。性能

一句話: 迴流必將引發重繪,重繪不必定會引發迴流。字體

性能影響

有時即便僅僅迴流一個單一的元素,它的父元素以及任何跟隨它的元素也會產生迴流。優化

現代瀏覽器會對頻繁的迴流或重繪操做進行優化:

瀏覽器會維護一個隊列,把全部引發迴流和重繪的操做放入隊列中,若是隊列中的任務數量或者時間間隔達到一個閾值的,瀏覽器就會將隊列清空,進行一次批處理,這樣能夠把屢次迴流和重繪變成一次。

當你訪問如下屬性或方法時,瀏覽器會馬上清空隊列:

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

由於隊列中可能會有影響到這些屬性或方法返回值的操做,即便你但願獲取的信息與隊列中操做引起的改變無關,瀏覽器也會強行清空隊列,確保你拿到的值是最精確的。

優化重繪迴流

  • 減小對 render tree 的操做 【合併屢次多DOM和樣式的修改】
  • 減小對一些style信息的請求,儘可能利用好瀏覽器的優化策略

CSS

  1. 直接改變className. (把要修改的樣式集中到一個 class 內統一修改);
  2. 避免使用 table 佈局 (儘可能不要使用表格佈局,若是沒有定寬表格一列的寬度由最寬的一列決定,那麼極可能在最後一行的寬度超出以前的列寬,引發總體迴流形成table可能須要屢次計算才能肯定好其在渲染樹中節點的屬性,一般要花3倍於同等元素的時間。)
  3. 儘量在DOM樹的最末端改變class,儘量在DOM樹的裏面改變class(能夠限制迴流的範圍)
  4. 將須要屢次重排的元素,position屬性設爲absolute或fixed,這樣此元素就脫離了文檔流,它的變化不會影響到其餘元素。例若有動畫效果的元素就最好設置爲絕對定位;
  5. 使用display:none技術,只引起兩次迴流和重繪;

JS

  1. 避免頻繁操做樣式,最好一次性重寫style屬性,或者將樣式列表定義爲class並一次性更改class屬性。(而不是利用js控制樣式)
  2. 不要常常訪問會引發瀏覽器緩存隊列的屬性(上述那些瀏覽器會馬上清空隊列的屬性)。若是確實要訪問,利用緩存。eg:
// 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";
}

複製代碼
  1. 儘可能將須要改變DOM的操做一次完成
let box = document.getElementById("box").style;
// bad
box.color = "red";    // 重繪
box.size = "14px";    // 迴流、重繪
// good
box.bord = '1px solid red'
複製代碼
  1. 對具備複雜動畫的元素使用絕對定位,使它脫離文檔流,不然會引發父元素及後續元素頻繁迴流。
  2. 避免頻繁操做 DOM,建立一個documentFragment,在它上面應用全部 DOM 操做,最後再把它添加到文檔中。

DocumentFragment,文檔片斷接口,表示一個沒有父級文件的最小文檔對象. 與 Document 最大的區別是由於 DocumentFragment 不是真實 DOM 樹的一部分,它的變化不會觸發 DOM 樹的(從新渲染) ,且不會致使性能等問題。

可使用 document.createDocumentFragment 方法或者構造函數來建立一個空的 DocumentFragment.

相關文章
相關標籤/搜索