記錄--瀏覽器渲染流程解析

来源:https://www.cnblogs.com/smileZAZ/archive/2022/07/08/16458533.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家可能經常會聽到 css 動畫比 js動畫性能更好這樣的論斷,或者是“硬體加速”,“層提升” 這樣的字眼;要瞭解這些內容就需要對瀏覽器的渲染流程有個大致的瞭解,本文就是我個人對這些內容的一個總結梳理 需要註意的是: 本文僅個人學習總結梳 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

大家可能經常會聽到 css 動畫比 js動畫性能更好這樣的論斷,或者是“硬體加速”,“層提升” 這樣的字眼;要瞭解這些內容就需要對瀏覽器的渲染流程有個大致的瞭解,本文就是我個人對這些內容的一個總結梳理

需要註意的是:

  1. 本文僅個人學習總結梳理,如有錯漏,望指正
  2. 本文以谷歌瀏覽器Blink內核為例
  3. 隨著谷歌瀏覽器的更新迭代,有些渲染流程或對象名詞可能發生變化(如, RenderObject 變成了 LayoutObject,RenderLayer 變成了 PaintLayer),查看相關文檔時需要註意文檔的時間

渲染流程

先來看下blink的一個大致渲染流程,圖源谷歌的一份共用幻燈片 Life of a Pixel ,它比較全面的闡述了瀏覽的渲染流程,非常值得一看,我們就借這張圖來梳理一遍

圖中分為 渲染進程(renderer process) 和 GPU進程(GPU process) 兩部分,其中渲染進程包含 主線程(main) 和 合成線程(impl)

我們可以藉助谷歌開發工具的 performance 標簽查看是否執行了某些渲染流程步驟,我這裡寫了一個簡單的html可以作為對比

<!DOCTYPE html>
<html lang="zh-cn">
​
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>transform demo</title>
</head>
<style>
  #normal {
    display: grid;
    place-items: end;
    width: 150px;
    height: 150px;
    background-color: pink;
  }
​
  #compositor {
    margin-top: 20px;
    display: grid;
    place-items: end;
    width: 150px;
    height: 150px;
    background-color: palegoldenrod;
  }
​
  #stacking {
    display: grid;
    place-items: end;
    width: 150px;
    height: 150px;
    position: absolute;
    z-index: -1;
    top: 240px;
    background-color: skyblue;
  }
​
  .active {
    animation: transformAni 2s both;
  }
​
  @keyframes transformAni {
    to {
      transform: translate(200px);
    }
  }
</style>
​
<body>
  <div id="compositor">Compositor Layers</div>
  <div style="display: flex; margin-top: 20px;">
    <div id="cssBtn" style="background-color:  palegoldenrod; width: 200px;">add css animation</div>
  </div>
​
  <div id="stacking">The Stacking Context</div>
  <div style="display: flex; margin-top: 220px;">
    <div id="jsBtn" style="background-color: skyblue; width: 200px;"> add js animation</div>
  </div>
​
  <script>
    const cssBtn = document.getElementById('cssBtn')
    const compositor = document.getElementById('compositor')
    cssBtn.addEventListener('click', () => {
      compositor.classList.add("active");
    })
​
    const jsBtn = document.getElementById('jsBtn')
    const stacking = document.getElementById('stacking')
    jsBtn.addEventListener('click', () => {
      setInterval(() => {
        stacking.style.left = `${stacking.getBoundingClientRect().left + 10}px`
      }, 100);
    })
​
  </script>
</body>
​
</html>

1. 構建DOM樹

對應頭圖中 DOM 節點,由於瀏覽器本身無法直接理解和使用html,所以需要將html轉換為瀏覽器能夠理解的DOM樹,也正是因此我們才能通過js控制dom節點

2. 樣式計算

對應頭圖中 style 節點,不僅是html,瀏覽器同樣無法直接讀懂我們寫的 css 。因此瀏覽器會將我們寫的 css 轉換成它能理解的 styleSheets ,同時計算每個 DOM 節點的樣式結果。包括將處理樣式的繼承覆蓋,將 rem 等相對單位轉換成 px,將 margin: 8 這樣的縮寫,拆開解析成 margin-left: 8margin-top: 8 等具體的值。可以通過 computed 標簽查看。

3. 佈局計算

對應頭圖中 layout 節點,這個階段也是我們很常聽到的 迴流(reflow),重排。在上兩個階段結束後會生成一個儲存其計算結果的樹結構 LayoutObject Tree。在這個階段瀏覽器會遍歷 LayoutObject Tree 計算每個節點在頁面上具體的佈局(比如是正常流佈局,或是flex佈局,哪個元素該放到哪個具體的像素位置上),計算文本實際寬高等;這一階段谷歌正在重構,目前輸入和輸出都混在 LayoutObject Tree 上,之後可能會將輸出部分抽離出來

4. 分層階段

對應頭圖中 comp.assign (compositing assignments) 節點,這個階段是我們獲取性能提升的關鍵。頁面上的元素,根據所處坐標空間(基本可以理解為層疊上下文)不同等原因,會被劃分為不同的 PaintLayer,通過分層的方式保證頁面上元素以正確的順序層疊;在此基礎上,某些特殊的PaintLayer 會被提升為合成層(Compositing Layers),每個合成層擁有單獨的 GraphicsLayer , 而沒有被提升的 PaintLayer 則與其祖先元素共用同一個 GraphicsLayer.

它們間的對應關係如下圖

每個 GraphicsLayer 都有一個 GraphicsContextGraphicsContext 負責輸出該層的點陣圖,即每層代表一份點陣圖,GPU將點陣圖合成渲染到屏幕上也就是我們看到的頁面

我們可以通過開發者工具的 Layer 標簽看到 GraphicsLayer 的分層,劃分 PaintLayer 和 提升為 GraphicsLayer 的條件具體可見 無線性能優化:Composite (需要註意層重疊,層壓縮問題)

比如我上面的例子中,我給橙色的 div 加上了 will-change:transform 導致了層提升,而藍色的 div 與 document 共用一個 GraphicsLayer;我們還可以在 Details 標簽看到層提升的具體原因還有記憶體消耗 (tips: 層提升原因還可以看 safari 瀏覽器開發者工具的 layers ,會更加具體)

5. Pre-paint

這一階段主要有兩個任務,一是判斷與上一次paint階段(見下)相比有哪些內容需要被更新,二是構建 property trees

Paint invalidation which invalidates display items which need to be painted.

Builds paint property trees.

property treesproperty 是指 translation, scale 等需要大量計算的屬性。將這些屬性抽離出來單獨管理,避免父元素的變動導致其子元素上所有的屬性都有全部重新計算,具體見 How cc Works

6. paint

繪製階段,這一階段即我們常說的重繪階段,但這一階段並不是執行實際的頁面繪製,而是依據頁面內容的層疊順序生成 繪製任務列表,詳見 layer 工具,滾動滑輪可以重播繪製過程,可以觀察到,同一層疊上下文情況下,先生成背景繪製任務,再生成元素內容繪製任務,再生成更高層級的層疊上下文元素的繪製任務;

主線程的任務到這裡基本結束,將繪製列表提交(commit)到合成線程

7. tiling

tiling 分塊,為 GPU光柵化做準備;光柵化是GPU根據繪製任務生成點陣圖,並將點陣圖儲存在記憶體中。大家可能聽過 CPU 光柵化的操作,這裡引用一段 How cc Works 中文譯文

Chromium 目前實際支持三種不同的光柵化和合成的組合方式:軟體光柵化 + 軟體合成,軟體光柵化 + gpu 合成,gpu 光柵化 + gpu 合成。在移動平臺上,大部分設備和移動版網頁使用的都是 gpu 光柵化 + gpu 合成的渲染方式,理論上性能也最佳

由於這一操作需要消耗較多資源,為了減少資源消耗和使頁面更快呈現會將圖層進行分塊( tiles ),將圖塊作為光柵化的基本單位,同時優先對視口附近的圖塊進行光柵化

通過rendering 標簽,勾選 layer borders 可以看到分塊情況,橙線是不同的 layer 而 青綠色的線則劃分了圖塊

8. raster

這一步由GPU執行光柵化操作,之後的節點我沒再深入瞭解,大概是光柵化生成draw quads 命令,該命令會引用光柵化結果最後將內容展現在屏幕上

總結

最後我們分別錄製兩個動畫的執行流程

js 動畫

 

可以看到 js 動畫在每次執行時會重排重繪,執行整個流程,上面橙紅色的那條前面有寫到 Layout Shift,即 佈局提升,也就是我們說的強制重排,因為我們在 js 腳本里執行了 stacking.getBoundingClientRect().left 訪問元素位置,這就需要立刻重排來計算元素當前的位置

css動畫

可以看到,css動畫主線程上沒有進行重排重繪

梳理完整個流程,我們就能理解開頭提到的內容了,關鍵點就在於分層合成

“層提升” 即文中的 分層階段;

“硬體加速” 即 GPU加速,一些可能導致頁面大範圍重排重繪(如 translate動畫),或需要大量簡單計算的任務(如 filter動畫)都會導致層提升,將這部分任務交由GPU處理,將處理完後的結果再合成到頁面上;

而 css 動畫性能更優的原因是:

  1. 避免了通過js訪問元素的位置信息導致強制重排
  2. css動畫元素移動時在合成層上進行,避免了頁面重排
  3. 合成由 GPU 進程式控制制,即使 js 阻塞主線程,css動畫也能正常執行

層提升會加大記憶體消耗,加大移動端設備負擔,需要酌情使用

補充

will-change

上文我們的例子提到了 will-change 屬性,它的作用是提前告知瀏覽器可能變動的屬性,讓瀏覽器提前做好準備,提前進行相關計算等,它有以下取值

  • auto 讓瀏覽器自己猜哪些值會變動
  • scroll-position 表示滾動條位置可能發生變化或產生動畫
  • contents 表示元素內容可能變動或產生動畫
  • <custom-ident> 表示所有css屬性

基本上哪裡的css屬性變化導致了頁面的卡頓都可以使用 will-change 優化

我們的例子中已經寫入了 will-change: transform ,因此瀏覽器一開始就幫我們做了層提升準備,所以橙色 div 一開始在頁面上就是分層的情況。而如果我們去掉這個屬性,觀察 layer 會發現橙色 div 一開始在頁面上並沒有層提升,只有在執行動畫時才進行了層提升,動畫結束後層提升又消失了

使用該屬性同樣要註意的是記憶體消耗問題,因為瀏覽器會提前進行優化計算並儲存計算結果。由於瀏覽器本身已經做了十足的性能優化,因此在頁面沒出現動畫卡頓之前沒有必要使用該屬性,如果需要使用也儘量通過以下形式:

.will-change-parent:hover .will-change {
  will-change: transform;
}
.will-change {
  transition: transform 0.3s;
}
.will-change:hover {
  transform: scale(1.5);
}

當父元素 hover 時,給子元素加上 will-change,hover 失效則移出,既給了瀏覽器準備的時間,又避免了一直掛著該屬性帶來的資源消耗

requestAnimationFrame / requestIdleCallback

講到動畫我們就順便提一嘴 requestAnimationFramerequestIdleCallback

我們看到的動畫都是由屏幕快速播放一系列連貫的圖片組成,為了讓人眼感受不到卡頓,大多數屏幕的刷新頻率都是60Hz,即一秒鐘刷新六十次屏幕,每次刷新叫做一幀,一幀時間大約16.7ms,如果一幀的渲染時間超過這個數就會導致動畫看起來出現了卡頓,一幀流程大致如下圖

requestAnimationFrame會在每一幀的渲染流程執行前都執行一次,因此使用js實現動畫時,相比於 setInterval 實際執行時間的不確定性requestAnimationFrame 更加可靠;

requestIdleCallback 則是在每一幀結束前判斷是否有剩餘時間,如果有則執行,無則不執行

本文轉載於:

https://juejin.cn/post/7116819495628472327

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文鏈接:三分鐘走進袋鼠雲一站式全自動化全生命周期運維管家ChengYing(承影) 課件獲取:關註公眾號 ** “數棧研習社”,後臺私信 “ChengYing”** 獲得直播課件 視頻回放:點擊這裡 ChengYing 開源項目地址:github 丨 gitee 喜歡我們的項目給我們點個** ST ...
  • 表的約束: 關鍵字:constraint 約束是一種表級別的限制,它通過對錶的數據限制來保證數據的完整性和一致性 常見約束: 主鍵約束(primary key) 用途:就是用來約束其中的一列,作為所有列中的標識符(這一列的唯一代表), 在一張表中通過主鍵可以準確定位到一列。可以避免列中數據的重覆。 ...
  • 近年來,各類欺詐案件屢見報端,“金融造富”的騙局防不勝防。網上銀行欺詐、電話銀行欺詐、網路欺詐、電信欺詐、盜刷銀行卡、POS機套現、貸款欺詐……龐大的利益誘惑下,金融詐騙黑產的水深不見底。各大銀行風控部門也一直在致力於迭代反欺詐系統守衛人民財產。 反詐困境 某國有大型銀行信用卡中心在與騰訊雲溝通時曾 ...
  • 互聯網時代下,網路直播帶貨流量數據爆發明顯,許多app的出海同時開拓了潛力巨大的海外網購市場,進行跨境電商直播既可以刺激商品消費,又可以把中國文化帶向世界。但目前跨境直播帶貨的問題在於語言問題,很多外國網友並不能聽懂中文主播所講的內容,要兼備外語與帶貨能力的主播又非常稀缺。 HMS Core機器學習 ...
  • 前言:CADisplayLink、NSTimer 迴圈引用問題 ​ CADisplayLink、NSTimer會對Target產生強引用,如果target又對他們產生強引用,那麼就會引發迴圈引用。 @interface ViewController () @property (nonatomic, ...
  • 本案例代碼背景如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewpo ...
  • CSS進階內容—浮動和定位詳解 我們在學習了CSS的基本知識和盒子之後,就該瞭解一下網頁的整體構成了 當然如果沒有學習之前的知識,可以到我的主頁中查看之前的文章:秋落雨微涼 - 博客園 CSS的三種佈局流派 網頁佈局的本質就是用CSS控制盒子的擺放來形成頁面 CSS提供了三種流派來控制盒子: 普通流 ...
  • 前言 ElementUI官方沒有提供菜單動態生成的案例的,參考ng-design上的菜單動態生成,寫了一個基於ElementUi的菜單動態生成,支持多級菜單。 思路 基本思路就是迴圈子組件,判斷menu是否有children,有就說明至少有二級菜單,需要迴圈子組件,將menu.children作為參 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...