瀏覽器渲染原理 (三)repaint(重繪)和reflow(迴流)詳解

滿足則不辱,知止則不殆。——老子javascript

瀏覽器渲染原理 (一)在網址中輸入一個網站後面都作了什麼css

瀏覽器渲染原理 (二)css、javascript、dom 阻塞關係html

瀏覽器渲染原理 (三) repaint(重繪)和 reflow(迴流)詳解前端

簡介

大多數設備的刷新頻率是60Hz,也就說是瀏覽器對每一幀畫面的渲染工做要在 16ms 內完成,超出這個時間,頁面的渲染就會出現卡頓現象,影響用戶體驗。 repaint/重繪reflow/迴流發生在什麼渲染的那個階段,咱們要了解什麼叫作 repaint/重繪reflow/迴流.java

(重繪)

repaint 就是在不影響排版的狀況下對這個元素從新繪製的過程。例如改變一個元素的背景顏色、字體顏色等。jquery

reflow(迴流、重排)

當 render tree 中的一部分(或所有)由於元素的規模尺寸,佈局,隱藏等改變而須要從新構建。這就稱爲迴流(其實我以爲叫從新佈局更簡單明瞭些)。每一個頁面至少須要一次迴流,就是在頁面第一次加載的時候。 迴流必將引發重繪,而重繪不必定會引發迴流chrome

三種常見的渲染流程

1. JS/CSS>計算樣式>佈局>繪製>渲染層合併瀏覽器

reflow
這張圖上渲染流程對應的是 reflow渲染的過程,它會通過佈局再繪製。

2. JS/CSS>計算樣式>繪製>渲染層合併 緩存

repaint
這張圖上渲染流程對應的是 repaint渲染的過程,它不須要通過佈局,只須要繪製當前的元素,不須要從新計算它的父元素。

3. JS/CSS>計算樣式>渲染層合併 dom

compositor
這張圖上渲染流程比較特殊,它不選通過佈局、繪製,它只須要在 合成層上修改。

repaint(重繪)和 reflow(迴流)


上面大體已經記錄了 repaint、reflow 的流程和爲何要關注它,下面記錄一下它們的觸發條件、和 eventLoop 的關係。

觸發 repaint、reflow

  1. 添加、刪除元素(迴流+重繪)
  2. 隱藏元素,display:none(迴流+重繪),visibility:hidden(只重繪,不迴流)
  3. 移動元素,好比改變 top、left(jquery 的 animate 方法就是改變 top、left 不必定會影響迴流),或者移動元素到另外 1 個父元素中。(重繪+迴流)
  4. 對 style 的操做(對不一樣的屬性操做,影響不同)(color、background-color)=>(重繪) (padding、margin)=>(迴流)
  5. 瀏覽器大小改變resizefont-size(重繪+迴流)
  6. transform/opacity (不會觸發生重繪、迴流)
  7. 最複雜的一種:獲取某些屬性,引起迴流 不少瀏覽器會對迴流作優化,他會等到足夠數量的變化發生,在作一次批處理迴流。 可是除了 render 樹的直接變化。 當獲取一些屬性時,瀏覽器爲了得到正確的值也會觸發迴流。
    1. offsetTop, offsetLeft, offsetWidth, offsetHeight
    2. scrollTop/Left/Width/Height
    3. clientTop/Left/Width/Height
    4. width,height
    5. 調用了 getComputedStyle(), 或者 IE 的 currentStyle

上面大體就是觸發repaint、reflow的操做,還有更多的後面還會補全。

repaint、reflow 和 eventLoop 關係

  1. 當 **Event loop 執行完 Microtasks 後,會判斷 document 是否須要更新。由於瀏覽器是 60Hz 的刷新率,每 16ms 纔會更新一次。
  2. 而後判斷是否有 resize 或者 scroll ,有的話會去觸發事件,因此 resize 和 scroll 事件也是至少 16ms 纔會觸發一次,而且自帶節流功能。
  3. 判斷是否觸發了 media query
  4. 更新動畫而且發送事件
  5. 判斷是否有全屏操做事件
  6. 執行 requestAnimationFrame 回調
  7. 執行 IntersectionObserver 回調,該方法用於判斷元素是否可見,能夠用於懶加載上,可是兼容性很差
  8. 更新界面

在這個裏面記錄了觸發 repaing、reflow,還有和 eventloop 的關係,可是 eventloop 和 ui 渲染流程關係有點太複雜,因此大體記錄了一下。

優化渲染性能


  • 減小重繪和迴流
  • 優化 JavaScript 的執行效率
  • 對用戶輸入事件的處理函數去抖動
  • 優先使用渲染層合併屬性、控制層數量
  • 結合 chrome 工具分析性能

減小重繪和迴流

  • 避免逐項更改樣式。最好一次性更改 style 屬性,或者將樣式列表定義爲 class 並一次性更改 class 屬性。
  • 避免循環操做 DOM。建立一個 documentFragment 或 div,在它上面應用全部 DOM 操做,最後再把它添加到 window.document。
  • 避免屢次讀取 offsetLeft 等屬性。沒法避免則將它們緩存到變量。
  • 將複雜的元素絕對定位或固定定位,使它脫離文檔流。不然迴流代價十分高
  • 不要使用 table 佈局,可能很小的一個小改動會形成整個 table 的從新佈局
  • 動畫實現的速度的選擇,動畫速度越快,迴流次數越多,也能夠選擇使用 requestAnimationFrame
  • CSS 選擇符從右往左匹配查找,避免 DOM 深度過深
  • 使用 visibility 替換 display: none ,由於前者只會引發重繪,後者會引起迴流(改變了佈局)
  • 使用 translate 替代 top
  • 把 DOM 離線後修改,好比:先把 DOM 給 display:none (有一次 Reflow),而後你修改 100 次,而後再把它顯示出來
  • 使用 flexbox 替代老的佈局模型 還有不少在這裏就不一一列舉了,主要思想就是減小 reflow、repaint 的次數。

優化 JavaScript 的執行效率

動畫實現,避免使用 setTimeout 或 setInterval,儘可能使用 requestAnimationFrame 把耗時長的 JavaScript 代碼放到 Web Workers 中去作

requestAnimationFrame

動畫實現,避免使用 setTimeout 或 setInterval,儘可能使用 requestAnimationFrame setTimeout(callback)和 setInterval(callback)沒法保證 callback 函數的執行時機,極可能在幀結束的時候執行,從而致使丟幀,以下圖:

setTimeout、和setInterval

requestAnimationFrame(callback)能夠保證 callback 函數在每幀動畫開始的時候執行。

注意:jQuery 的 animate 函數就是用 setTimeout 來實現動畫,能夠經過 jquery-requestAnimationFrame 這個補丁來用 requestAnimationFrame 替代 setTimeout

把耗時長的 JavaScript 代碼放到 Web Workers 中去作

把耗時長的 JavaScript 代碼放到 Web Workers 中去作 JavaScript 代碼運行在瀏覽器的主線程上,與此同時,瀏覽器的主線程還負責樣式計算、佈局、繪製的工做,若是 JavaScript 代碼運行時間過長,就會阻塞其餘渲染工做,極可能會致使丟幀。 前面提到每幀的渲染應該在 16ms 內完成,但在動畫過程當中,因爲已經被佔用了很多時間,因此 JavaScript 代碼運行耗時應該控制在3-4 毫秒

對用戶輸入事件的處理函數去抖動

用戶輸入事件處理函數會在運行時阻塞幀的渲染,而且會致使額外的佈局發生。 理想狀況下,當用戶和頁面交互,頁面的渲染層合併線程將接收到這個事件並移動元素。這個響應過程是不須要主線程參與,不會致使 JavaScript、佈局和繪製過程發生。

去抖

可是若是被觸摸的元素綁定了輸入事件處理函數,好比 touchstart/touchmove/touchend,那麼渲染層合併線程必須等待這些被綁定的處理函數執行完畢才能執行,也就是用戶的滾動頁面操做被阻塞了,表現出的行爲就是滾動出現延遲或者卡頓。 簡而言之就是你必須確保用戶輸入事件綁定的任何處理函數都可以快速的執行完畢,以便騰出時間來讓渲染層合併線程完成他的工做。

去抖

避免使用運行時間過長的輸入事件處理函數

輸入事件處理函數,好比 scroll/touch 事件的處理,都會在 requestAnimationFrame 以前被調用執行。 所以,若是你在上述輸入事件的處理函數中作了修改樣式屬性的操做,那麼這些操做就會被瀏覽器暫存起來,而後在調用 requestAnimationFrame 的時候,若是你在一開始就作了讀取樣式屬性的操做,那麼將會觸發瀏覽器的強制同步佈局操做。

去抖

對滾動事件處理函數去抖動

經過 requestAnimationFrame 能夠對樣式修改操做去抖動,同時也可使你的事件處理函數變得更輕

function onScroll(evt) {
  // Store the scroll value for laterz.
  lastScrollY = window.scrollY;

  // Prevent multiple rAF callbacks.
  if (scheduledAnimationFrame) {
    return;
  }

  scheduledAnimationFrame = true;
  requestAnimationFrame(readAndUpdatePage);
}

window.addEventListener("scroll", onScroll);
複製代碼

chrome 分析結合

使用 Chrome DevTools 來了解頁面的渲染層狀況

Chrome DevTools > Preformance

經過 Chrome DevTools 的 Preformance 來錄製一段操做,而且開啓 More tools 的 Layers、Rendering 功能,若是下圖所示:

Chrome DevTools

Chrome DevTools

藉助 Chrome 暴露在 window 上的 preformance 對象

能夠用 preformance 對象來作一些前端的性能分析和統計,以下圖所示:

Chrome DevTools

總結

在這個上面瞭解了 reflow、repaint流程和他的觸發條件,再後面也有一部分怎麼優化性能,結合前面的渲染流程大體知道優化的是那部分的流程。固然在優化性能的時候,必定要結合 chrome 的性能分析功能。

參考

深度剖析瀏覽器渲染性能原理,你到底知道多少?

迴流(reflow)與重繪(repaint)

高性能 WEB 開發:深刻理解頁面呈現、重繪、迴流

相關文章
相關標籤/搜索