前端面試查漏補缺--(五) 渲染機制及重繪和迴流

shotCat發表於2019-02-22

前言

本系列最開始是為了自己面試準備的.後來發現整理越來越多,差不多有十二萬字元,最後決定還是分享出來給大家.

為了分享整理出來,花費了自己大量的時間,起碼是隻自己用的三倍時間.如果喜歡的話,歡迎收藏,關注我!謝謝!

文章連結

合集篇:

前端面試查漏補缺--Index篇(12萬字元合集) 包含目前已寫好的系列其他十幾篇文章.後續新增值文章不會再在每篇新增連結,強烈建議議點贊,關注合集篇!!!!,謝謝!~

後續更新計劃

後續還會繼續新增設計模式,前端工程化,專案流程,部署,閉環,vue常考知識點 等內容.如果覺得內容不錯的話歡迎收藏,關注我!謝謝!

求一份內推

目前本人也在準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的武漢 前端崗位!郵箱:bupabuku@foxmail.com.謝謝啦!~

渲染機制

渲染步驟

瀏覽器的渲染機制一般分為以下幾個步驟:

    1. 處理 HTML 並構建 DOM 樹。
    1. 處理 CSS 構建 CSSOM 樹。
    1. 將 DOM 與 CSSOM 合併成一個渲染樹。
    1. 根據渲染樹來佈局,計算每個節點的位置。
    1. 呼叫 GPU 繪製,合成圖層,顯示在螢幕上。

注意:

  • 在構建 CSSOM 樹時,會阻塞渲染,直至 CSSOM 樹構建完成。並且構建 CSSOM 樹是一個十分消耗效能的過程,所以應該儘量保證層級扁平,減少過度層疊,越是具體的 CSS 選擇器,執行速度越慢
  • 當 HTML 解析到 script 標籤時,會暫停構建 DOM, 完成後才會從暫停的地方重新開始。也就是說,如果你想首屏渲染的越快,就越不應該在首屏就載入 JS 檔案。並且 CSS 也會影響 JS 的執行,只有當解析完樣式表才會執行 JS,所以也可以認為這種情況下,CSS 也會暫停構建 DOM。

Load 和 DOMContentLoaded 區別

  • Load 事件觸發代表頁面中的 DOM,CSS,JS,圖片已經全部載入完畢。
  • DOMContentLoaded 事件觸發代表初始的 HTML 被完全載入和解析,不需要等待 CSS,JS,圖片載入。

圖層

一般來說,可以把普通文件流看成一個圖層。特定的屬性可以生成一個新的圖層。不同的圖層渲染互不影響,所以對於某些頻繁需要渲染的建議單獨生成一個新圖層,提高效能。但也不能生成過多的圖層,會引起反作用。

通過以下幾個常用屬性可以生成新圖層

  • 3D 變換:translate3dtranslateZ
  • will-change
  • videoiframe 標籤
  • 通過動畫實現的 opacity 動畫轉換
  • position: fixed

重繪(Repaint)和迴流(Reflow)

概念

重繪和迴流是渲染步驟中的一小節,但是這兩個步驟對於效能影響很大。

  • 重繪是 當節點需要更改外觀而不會影響佈局的,比如改變 color、background-color、visibility等就叫稱為重繪
  • 迴流是 佈局或者幾何屬性需要改變 就稱為迴流。

注意: 迴流必定會發生重繪,重繪不一定會引發迴流。迴流所需的成本比重繪高的多,改變深層次的節點很可能導致父節點的一系列迴流。

會導致迴流的操作:

  • 頁面首次渲染
  • 瀏覽器視窗大小發生改變
  • 元素尺寸或位置發生改變
  • 元素內容變化(文字數量或圖片大小等等)
  • 元素字型大小變化
  • 新增或者刪除可見DOM元素
  • 啟用CSS偽類(例如::hover
  • 查詢某些屬性或呼叫某些方法

一些常用且會導致迴流的屬性和方法:

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

重繪和迴流與Event loop關係

很多人不知道的是,重繪和迴流其實和 Event loop 有關。

  1. 當 Event loop 執行完 Microtasks 後,會判斷 document 是否需要更新。因為瀏覽器是 60Hz 的重新整理率,每 16ms 才會更新一次。
  2. 然後判斷是否有 resize 或者 scroll ,有的話會去觸發事件,所以 resize 和 scroll 事件也是至少 16ms 才會觸發一次,並且自帶節流功能。
  3. 判斷是否觸發了 media query
  4. 更新動畫並且傳送事件
  5. 判斷是否有全屏操作事件
  6. 執行 requestAnimationFrame 回撥
  7. 執行 IntersectionObserver 回撥,該方法用於判斷元素是否可見,可以用於懶載入上,但是相容性不好
  8. 更新介面
  9. 以上就是一幀中可能會做的事情。如果在一幀中有空閒時間,就會去執行 requestIdleCallback 回撥。

減少重繪和迴流

  • 使用 translate 替代 top
  • 使用 visibility 替換 display: none ,因為前者只會引起重繪,後者會引發迴流(改變了佈局)

  • 把 DOM 離線後修改,比如:先把 DOM 給 display:none (有一次 Reflow),然後你修改 100 次,然後再把它顯示出來

  • 不要把 DOM 結點的屬性值放在一個迴圈裡當成迴圈裡的變數

  • 不要使用 table 佈局,可能很小的一個小改動會造成整個 table 的重新佈局

  • 動畫實現的速度的選擇,動畫速度越快,迴流次數越多,也可以選擇使用 requestAnimationFrame

  • CSS 選擇符從右往左匹配查詢,避免 DOM 深度過深

  • 將頻繁執行的動畫變為圖層,圖層能夠阻止該節點回流影響別的元素。比如對於 video 標籤,瀏覽器會自動將該節點變為圖層。

CSS

  • 避免使用table佈局。
  • 儘可能在DOM樹的最末端改變class
  • 避免設定多層內聯樣式。
  • 將動畫效果應用到position屬性為absolutefixed的元素上。
  • 避免使用CSS表示式(例如:calc())。

JavaScript

  • 避免頻繁操作樣式,最好一次性重寫style屬性,或者將樣式列表定義為class並一次性更改class屬性。
  • 避免頻繁操作DOM,建立一個documentFragment,在它上面應用所有DOM操作,最後再把它新增到文件中。
  • 也可以先為元素設定display: none,操作結束後再把它顯示出來。因為在display屬性為none的元素上進行的DOM操作不會引發迴流和重繪。
  • 避免頻繁讀取會引發迴流/重繪的屬性,如果確實需要多次使用,就用一個變數快取起來。
  • 對具有複雜動畫的元素使用絕對定位,使它脫離文件流,否則會引起父元素及後續元素頻繁迴流。

感謝及參考

整篇文章基本都是摘自下面文章,這裡表示感謝!

相關文章