從瀏覽器原理分析界面性能優化02---萬字長文弄懂瀏覽器渲染原理

從瀏覽器原理分析界面性能優化 02—界面渲染

前言

說到性能優化,界面渲染優化是我們要注意的重中之重.想要優化我們的界面渲染,我們首先要明白界面渲染的具體流程.

我們先從幾個面試中經常出現的題目來切入這個問題:

  • JS 解析會阻塞界面渲染麼?
  • CSS 的加載和解析會阻塞界面渲染麼?
  • 簡要描述瀏覽器的重繪和迴流
  • 什麼是 GPU 加速?
  • 什麼是異步加載和預加載?
  • 什麼是關鍵渲染路徑(CRP:Critical Rendering Path)?

上面幾個問題,大都是和瀏覽器的界面渲染相關的,我們先不着急去解答這些問題,我們先探究一下瀏覽器的渲染流程,在回頭看看這幾個問題.

瀏覽器的宏觀架構

先說一下什麼是進程和線程:

進程是 CPU 分配資源的最小單位(能夠擁有資源和獨立運行的最小單位)
線程是 CPU 調度的最小單位(運行在進程上的一次程序運行單位)

我們再看一下瀏覽器的架構:
在這裏插入圖片描述
瀏覽器的架構是多進程架構,這個之前的文章有介紹過,大家有興趣的可以去看看

從瀏覽器原理分析界面性能優化 00

瀏覽器內核(瀏覽器渲染進程)

我們常說的瀏覽器內核也就是瀏覽器的渲染進程,它主要包含了 JS 引擎線程、GUI 線程、事件觸發線程等,我們先看看各個線程的工作內容

GUI 線程

  • GUI 線程主要負責瀏覽器的界面渲染,HTML 文件、CSS 文件解析,DOM 樹、渲染樹、分層樹的構建等
  • GUI 線程也負責我們常說的迴流、重繪的過程
  • GUI 線程和 JS 線程是互斥的,當 GUI 線程運行的時候 JS 線程會被掛起,反之亦然

JS 引擎線程

  • JS 引擎線程即 JS 內核,負責處理 JS 腳本程序,
  • JS 線程中也負責 JS 的垃圾回收工作
  • JS 也負責等待任務隊列中的任務到來,然後進行處理
  • JS 線程是單線程的
  • JS 線程和 GUI 線程是互斥的,當 JS 線程運行的時候 GUI 線程會被掛起,反之亦然

事件觸發線程

  • 事件觸發線程主要負責維護一個事件隊列
  • 當我們的操作或者 JS 腳本產生宏任務的時候會添加到事件隊列的隊尾

定是觸發器線程

  • 負責處理 setTimeout、setImmediate 等計時器
  • 因爲我們的 JS 線程執行代碼時是先解析代碼,然後再一行一行執行,因爲 JS 線程出現阻塞時可能影響定時器的準確性.所以這時候需要我們使用一個單獨的線程來處理計時器工作
  • 注意,在瀏覽器中 setTimeout 的最低觸發時間間隔爲 4ms

異步 http 請求線程

  • 在 XMLHttpRequest 連接後通過瀏覽器新開一個線程
  • 檢測到狀態變更時,回調函數會放到異步隊列中等待 JS 線程的執行

瀏覽器的渲染流程

下面我們看一下瀏覽器界面的渲染流程,這個過程發生在 HTTP 請求接收到數據之後,首先在這裏我們要提前建立一個清醒的認知:
我們從 HTTP 請求回來開始,這個過程並非一般想象中的一步做完再做下一步,而是一條流水線
有了這個認知,我們接上一篇文章從瀏覽器原理分析界面性能優化—瀏覽器的網絡請求之後,看看瀏覽器之後的操作:

準備渲染進程

當我們的網絡請求的響應頭信息的Content-Typetext/html時,瀏覽器則將繼續進行導航流程,創建我們的渲染進程.
默認情況下瀏覽器會爲每個標籤頁分配一個渲染進程,但在某些情況下瀏覽器也會將同屬於一個域名下的多個界面分配到同一個渲染進程中.多個頁面分配到同一進程中的操作叫做同一站點策略.
在這裏我們可以先理解一下什麼是同源站點:

我們將同一站點稱爲根域名(demo.com)和協議(https 或者 http),還包含了該根域名下的所有子域名和不同的端口.例如:

  • https://www.demo.com
  • https://time.demo.com
  • https://www.demo.com:8081
    他們都是同一站點,因爲他們的協議都是 https,根域名都是 demo.com

渲染進程策略

當我們打開新界面採用的渲染進程策略就是:

  • 正常打開新的界面,就會創建新的渲染進程
  • 如果新打開的界面和之前存在的界面是同一站點,那麼就會複用之前的渲染進程
  • 如果新打開的界面和之前的界面不屬於同源站點,則創建新的進程

提交文檔

在該階段,瀏覽器將網絡進程接收到的 html 數據提交給渲染進程:

  • 瀏覽器進程接收到網絡進程的響應頭數據之後,向渲染進程發送提交文檔的消息
  • 渲染進程,接收到提交文檔的消息之後,與網絡進程建立傳輸數據管道
  • 文檔數據傳輸完成之後,渲染進程會提交發送確認提交的消息給瀏覽器進程
  • 瀏覽器進程接收到確認提交的消息之後會更新界面,包括地址欄 URL、回退欄狀態、Web 界面等

PS:注意我們之前說過,渲染過程是一個流水線的狀態.所以我們上面只是爲了描述方便簡化了提交文檔的流程

渲染階段

當渲染進程接收到提交文檔的消息後,便開始界面解析的過程,下面我們先總結一下大概的過程:

在這裏插入圖片描述

  • 解析 HTML 文件,構建 DOM 樹,同時下載解析 CSS
  • CSS 解析成 CSS Tree 後和 DOM Tree 合成爲 Layout Tree
  • Layout Tree 進行界面元素的尺寸位置信息的確認(迴流過程發生在這裏),和元素的像素信息的繪製(重繪流程發生在這裏)
  • Layout Tree 得到界面個元素信息後會生成分層樹
  • 分層樹對各層進行分塊處理後開始位圖的繪製(這步在 GPU 中完成)
  • 位圖繪製完成後通知瀏覽器進程,將 Web 界面展示出來

解析 HTML 生成 DOM 樹

首先,說一下爲什麼要把 HTML 構建爲 DOM 樹,因爲 HTML 在瀏覽器中是以字符串的形式存在的,並不能被瀏覽器識別,所以需要將其轉換爲瀏覽器能夠理解的結:DOM 樹

我們來先看一下 DOM 樹是什麼樣的結構,這裏無恥的用了一下網上其他文章的圖片(侵刪):
在這裏插入圖片描述

上面的過程大概是一個 HTML 文件通過 HTML 解析器轉換爲 DOM 樹的過程.
我們都知道,HTML 被稱爲“超文本標記語言”,以下面的代碼爲例:

<p>內容</p>

代碼中的“p”標籤被稱爲標記,“內容”被稱爲文本,也就是說HTML 是由文本和標記組成的.
PS:這裏再次強調一下之前提到的一點:瀏覽器的渲染是一個流水線的狀態,所以我們的 HTML 解析也是從網絡進程那裏接收到多少數據便解析多少數據(便接收邊解析).
下面我們簡要來探討一下 HTML 生成 DOM 樹的過程:

先上一張我們的一個大致流程圖如下:

在這裏插入圖片描述

字節流轉換爲 token

該階段起主要作用的是分詞器,分詞器會將字節流轉換成一個個的 token,我們可以將其簡單分爲 Tag Token 和文本 Token,例如我們下面這段代碼:

<html>
  <body>
    <div>test</div>
  </body>
</html>

我們可以通過分詞器將上面代碼分爲如下 Token
在這裏插入圖片描述

上圖中我們可以看出來,Start Tag Token 和 End Tag Token 一一對應,同時文本 Token 是一個單獨的 Tag.

Token 轉換爲 DOM 節點,DOM 節點添加到 DOM 樹上

這其實是兩步,分別爲 Token 轉換爲 DOM 節點,DOM 節點添加到 DOM 樹中,不過正像我們之前提到的那樣,這是一個流水線式的過程,而且爲了方便理解我們將其合併在一起.
Token 轉換爲 DOM 樹,需要維護一個 Token 棧來完成這一過程,體轉換流程如下

  • 最開始的時候生成一個 document 跟節點,同時將一個 document Start Token 壓入棧
  • 然後處理我們分詞器解析出來的 Token
  • 如果是一個 Start Token 則入棧,創建一個 DOM 節點掛載到 DOM 樹上
  • 如果是一個 End Token,同時棧頂是一個對應的 End Token,那麼則棧頂的 Token 出棧
  • 如果分詞器解析出來的是一個文本 Token,那麼會生成一個文本節點掛載到 DOM 樹中(注意:文本 Token 不會入棧,他的父節點就是當前掛載的 DOM 節點)
  • 當我們所有的字節流通過分詞器解析完成,同時 Token 棧清空的時候,我們的 DOM 樹則構建完成

樣式計算

DOM 樹的生成是需要 CSS 和 HTML 共同作用的,也就是說樣式計算髮生在 DOM 樹完全生成之前.

樣式計算主要是解析 CSS,計算出 DOM 節點的具體樣式,該階段可以大致分爲三個階段

轉換 CSS

衆所周知,我們的 CSS 樣式來源主要有下面三個方面:

  • 通過 link 標籤外部引入
  • style 標籤的內聯樣式
  • 元素的內嵌行內樣式

這些樣式在解析之前也是以字節流的形式存儲在瀏覽器中的,渲染引擎接收到 CSS 文本時,會先執行轉換操作,將字節流轉換成瀏覽器能夠識別的結構:StyleSheets.

標準化屬性值

這步的主要目的是將各種 CSS 樣式,轉換爲渲染引擎能夠理解的、標準化的值.例如:
在這裏插入圖片描述

這個過程就稱爲屬性值標準化

計算出 DOM 節點的具體樣式

這步主要是計算出 DOM 節點的樣式然後附加到對應的節點上.
這裏樣式計算用到了繼承規則層疊規則:

  • 繼承規則:每個 DOM 節點都包含父節點的樣式(這個地方可以類比我們平常說的樣式繼承)
  • 層疊規則:樣式層疊規定了如何合併合併來自多個源的屬性值的算法.感興趣的同學可以去查一下層疊樣式表

佈局階段

我們現在有了 DOM 樹和 DOM 節點的對應樣式,但是這個時候瀏覽器還缺少關鍵的一點:每個元素的位置信息,接下來渲染引擎需要計算出每個可見元素的幾何位置,這個過程稱爲佈局.

這個階段需要兩個步驟:創建佈局樹和佈局計算

創建佈局樹(Layout Tree)

佈局樹的主要作用就是將可見元素構建爲一棵樹.大致包含下面兩個內容:

  • 遍歷 DOM 樹的所有節點,然後將可見的節點添加到佈局樹中
  • 不可見的節點會被忽略掉
佈局計算

這個階段的主要作用是計算每個元素的幾何位置信息,然後保存在佈局樹中.

分層

分層階段是在佈局樹的基礎上完成的.界面中有很多複雜的效果,爲了方便這些效果的實現,渲染引擎需要爲特定的節點生成特殊的圖層,並生成一棵對應的圖層樹.
這裏的圖層樹我們可以類比我們學過的層級上下文的概念.

從上面的描述我們可以看出來,瀏覽器的頁面其實分成好多圖層,這些圖層疊加後形成了最終的界面.

PS:不是每個節點都歸屬一個圖層,如果一個節點沒有對應的圖層,那麼他會屬於父節點的圖層.

滿足下面的規則會提升爲單獨的圖層:

  • 擁有層疊上下文屬性的元素會提升爲單獨的圖層
  • 需要裁剪的地方也會被創建爲圖層

圖層繪製

圖層樹構建完成後,渲染引擎會對每個圖層進行繪製.這個過程其實很簡單.假設一種情況:我們在一個黑色的背景上繪製一個紅色的矩形,矩形裏面有一個黃色的圓.
那麼我們的繪製過程可以大概描述爲:先繪製一個黑色的背景,然後再根據位置信息繪製一個矩形,然後在根據圓的位置信息繪製一個黃色的圓.
渲染引擎的繪製過程與上面的過程類似,它會把圖層的繪製拆分成一個個繪製指令,然後這些繪製指令會組合成一個繪製列表.

繪製列表只是用來描述圖層元素的繪製信息,但是真正完成繪製操作的是有渲染引擎的合成線程來完成.

分塊

繪製列表完成後,交給合成線程來進行繪製.
這裏我們首先要明確一個概念,一個網頁的頁面可能會很大,但是我們用戶看到的只是其中一部分,這部分我們稱之爲視口.
基於上述原因,合成線程會將界面進行分塊.圖塊的大小通常是 256256 或者 512512.合成線程會優先將視口附近的圖塊生成位圖.

光柵化

實際生成位圖的操作由柵格化來執行也稱爲光柵化,圖塊是執行柵格化操作的最小單位.柵格化的操作都是在柵格化線程池中完成,渲染引擎會維護一個柵格化線程池.

通常情況下,柵格化的操作都會使用 GPU 加速來完成,GPU 生成位圖的操作又叫做快速柵格化或者GPU 柵格化,生成的位圖會保存在 GPU 中.
PS:記得我們之前講過,瀏覽器分爲渲染進程GPU 進程,也就是說我們的快速柵格化的過程涉及到了跨進程通信

合成和顯示

所有的圖塊都光柵化處理完成後,合成線程就會生成一個繪製圖塊的命令:“DrawQuad”,然後將該命令提交到瀏覽器進程.
瀏覽器中有一個叫做 viz 的組件接收合成線程的“DrawQuad”命令,然後根據該命令將相應的命令將內容繪製到內存中,最後將內存中的界面顯示到屏幕上.

到這裏,經過一系列的階段,我們終於能在瀏覽器上看到了我們的界面了.

題解

1、JS 解析會阻塞界面的渲染麼

首先,我們要回憶一下之前的一個知識點:JS 線程和渲染線程是互斥的,當一方執行,另一方會被暫時掛起等待對方執行完畢後再執行.
由此我們可以得出結論:JS 解析會阻塞界面的渲染
JS 的加載情況比較複雜,下面我們來分別討論一下:

首先,在 html 中間插入一段 JS 腳本:

<html>
  <body>
    <div id="test">test1</div>
    <script>
      let div1 = document.getElementsById('test')
      div1.innerText = '測試'
    </script>
    <div>test2</div>
  </body>
</html>

前面的 html 解析和我們之前提到過的是相同的,但是到了 script 標籤之後,渲染引擎會被暫時掛起,然後 JS 腳本開始執行,腳本執行完以後 id 爲“test”的 div 的內容就會改爲“測試”.
然後,渲染引擎恢復執行渲染剩餘 html 文本.
理解了上面的渲染流程後,我們在看一個類似的:

內斂 JS 腳本換成 JS 引入的文件
<html>
  <body>
    <div>1</div>
    <script type="text/javascript" src="foo.js"></script>
    <div>test</div>
  </body>
</html>

上面把 script 的內斂腳本換成了一個外部引入的文件,但是內容完全相同.但是和之前不同的是 JS 外部腳本需要下載,這個JS 下載過程也會阻塞 DOM 解析.
但是,瀏覽器在這裏做了優化,即預解析操作:當瀏覽器接收到 html 字節流的時候,會開啓一個預解析線程,用來解析其中包含的 JS 和 CSS 文件,解析到相關文件後預解析線程會提前下載這些文件.
所以,JS 的下載和解析會阻塞 DOM 的解析,我們可以通過利用 CDN 加速、async/defer、preload 等策略來降低阻塞時間.

第三種情況

下面看一下 CSS 加入後產生的變化:

<html>
  <head>
    <style src="theme.css"></style>
    <style>
      div {
        color: blue;
      }
    </style>
  </head>
  <body>
    <div>1</div>
    <script>
      let div1 = document.getElementsByTagName('div')[0]
      div1.innerText = 'time.geekbang' //需要DOM
      div1.style.color = 'red' //需要CSSOM
    </script>
    <div>test</div>
  </body>
</html>

我們可以看到 JS 腳本中操作了 CSS 樣式,所以在 JS 腳本執行前需要等待 CSS 加載完畢並解析成 Style Sheets 之後才能執行.所以這種情況下CSS 會阻塞 JS 腳本的執行.

2、CSS 的加載和解析會阻塞界面渲染麼

從上述瀏覽器渲染流程我們可以看出,Layout Tree 的構建是由 HTML DOM 和 Style Sheets 共同構建完成的.
而且我們的 CSS 加載過程是一個異步的過程,所以CSS 的加載不會阻塞 DOM 的構建.
但是,Layout Tree 的構建是由 HTML DOM 和 Style Sheets 共同完成的,所以CSS 的加載會阻塞界面的渲染.

同時,我們知道 JS 是可以操作界面的 DOM 和 CSS 樣式的,所以瀏覽器在解析 JS 的時候需要 CSS 加載解析完成,同時JS 線程和渲染線程是互斥的.
也就是說,當我們解析 JS 的時候會阻塞 DOM 的渲染,同時 JS 的解析也需要 CSS 的樣式表.也就是說CSS 可以通過阻塞 JS 的解析來變相的阻塞 DOM 的渲染

3、簡要描述瀏覽器的重繪和迴流

通過上面的描述我們可以知道,瀏覽器的渲染流程的佈局階段包括了

  • Layout Tree 構建
  • 確定元素的幾何位置信息(會觸迴流發該過程)
  • 確定元素的像素信息(重繪會觸發該過程)

因此,我們可以有一個清醒的認知:迴流必引發重繪,重繪不會引發迴流

觸發迴流和重繪的操作有很多,大致可以分爲下面兩類:

  • 影響元素在文檔流中的位置和影響元素的幾何屬性的會產生迴流操作
  • 影響元素自身像素信息的操作會產生重繪操作

PS:當我們獲取元素的幾何或者位置信息時,因爲瀏覽器要返回給我們精確的信息,所以也會引起迴流操作

4、什麼是 GPU 加速

從上面的瀏覽器渲染流程我們可知,GPU 加速是直接跳過迴流和重繪的過程,執行圖層繪製和複合的操作.
因此,想使用 GPU 加速,需要將我們的元素變化操作提升到合成層,提升成合成層的操作有很多,這裏簡單介紹幾種:

  • CSS 中的 transform 屬性
  • 對 opacity 做 CSS 動畫
  • CSS 的 will-change(慎用,因爲瀏覽器的優化有很多,而該方法並不能提升多少性能)
  • CSS 的隱式合成

5、什麼是異步加載和預加載

異步加載:async/defer

異步加載可以將我們的 JS 腳本的下載改爲異步下載,也就是說 JS 的下載過程不會阻塞我們的 DOM 渲染.
但是這裏要注意async 和 defer 的區別:

  • async 是無序的,JS 腳本下載完以後會立即執行
  • defer 是有序的,JS 腳本下載完以後,會在 DOMContentLoaded 之前去按照加載順序執行腳本

從上面的區別可以看出,因爲 JS 腳本模塊化的特性,對無序的 async 的依賴並不大,

預加載

預加載 preload/prefetch,兩者都是 link 標籤中的 rel 屬性,不同的是

  • preload 提供聲明式的命令,讓瀏覽器提前加載資源,等到需要的時候再去執行
  • prefetch 是告訴瀏覽器下一個頁面纔會用到的資源,也就是說 prefetch 是爲了下一個界面的加速而存在的

簡單來說就是:preload 告訴瀏覽器必須提前加載的資源,prefetch 告訴瀏覽器可以提前加載的資源,兩者preload 的優先級高於 prefetch

6、什麼是關鍵渲染路徑

關鍵渲染路徑(Critical Rendering Path)即 CRP,關鍵渲染路徑說的是我們上面講過的瀏覽器首屏渲染所經歷的一系列的步驟.
它包含下面三部分內容:

  • 關鍵資源數量:即會阻塞首屏渲染的資源
  • 關鍵路徑長度:獲取所有關鍵資源所需要的往返的請求時間
  • 關鍵字節:首屏渲染所需要的總字節數,等同於所有關鍵資源文件的總和

前端優化

上面講了那麼多,終於繞回我們的主題了:前端性能優化.
想必同學們看到這裏可能有點累了.因此,我們在大致總結一下瀏覽器的渲染流程:

  • 解析 HTML 文件,構建 DOM 樹,同時下載解析 CSS
  • CSS 解析成 CSS Tree 後和 DOM Tree 合成爲 Layout Tree
  • Layout Tree 進行界面元素的尺寸位置信息的確認(迴流過程發生在這裏),和元素的像素信息的繪製(重繪流程發生在這裏)
  • Layout Tree 得到界面個元素信息後會生成分層樹
  • 分層樹對各層進行分塊處理後開始位圖的繪製(這步在 GPU 中完成)
  • 位圖繪製完成後通知瀏覽器進程,將 Web 界面展示出來

我們的前端性能優化可以順着上面的步驟來進行:

CSS 優化

關於 CSS 的優化,我們首先要明確一點:CSS 的匹配是從右向左進行匹配的,例如:

#list li {
}

按照我們一貫的思路肯定是以爲渲染引擎先匹配到“#list”,然後在去它下面查找“li”標籤,但實際上正好相反:渲染引擎必須遍歷每個 li 元素,然後再查找它的父元素的 id 是不是“list”,這是一個相當耗時的操作.
我們在看一個之前用過的:

* {
}

之前我們可能用通配符來清除樣式,但是通配符會遍歷所有的元素,因此通配符造成的開銷是非常大的,所以儘量避免使用通配符

綜上,所述我們平常在書寫 CSS 的時候可以有以下幾點性能優化方案:

  • 儘量避免使用通配符
  • 儘量避免使用標籤選擇器,因爲標籤選擇器會遍歷文檔下所有對應的標籤
  • 如果能使用繼承的樣式,儘量使用繼承,避免多次計算
  • 儘量減少不必要的嵌套,因爲嵌套會造成多次遍歷,增大了開銷
  • id 和 class 選擇器的使用要儘量簡潔,儘量不要和其他標籤搭配使用

渲染阻塞優化

通過之前的介紹我們可以知道,在頁面渲染的過程中,不合理的 JS、CSS 加載是會阻塞界面渲染的.
因此我們要做的就是合理安排腳本的加載順序,避免界面渲染的時候被阻塞.主要有以下幾點:

  • CSS 是阻塞渲染的資源,我們要儘可能早的下載,例如將 CSS 放到 head 標籤中,啓動 CDN 靜態資源的加載速度優化
  • JS 腳本要滯後加載,因爲對於首屏來說,即便沒有 JS,我們的界面呈現也不會受太大影響,所以我們可以將 JS 腳本放在文檔的最後,或者使用 async/defer 對其進行異步加載

減少 DOM 修改次數

這是一個關於線程間通訊的問題,通過上面的介紹我們知道了,JS 線程和渲染線程是不同的線程,所以當我們通過 JS 修改 DOM 的時候是需要進行線程間通訊的時候也會造成額外的開銷.
爲了避免這些無謂的開銷,我們可以將多次 DOM 操作合併爲一次,舉一個最典型的例子:

let wrapper = document.getElementsById('demo')
let fragment = document.createDocumentFragment()
for (let i = 0; i < 10; i++) {
  let p = document.createElement('p')
  p.innerText = `測試${i}`
  fragment.appendChild(p)
}
wrapper.appendChild(fragment)

上面的操作是利用Fragment的特性,將十次更新 DOM 的操作合併爲了一次

迴流與重繪的優化

通過上面的介紹我們知道,在我們操作 DOM 的時候如果引發迴流和重繪,那麼瀏覽器的開銷是非常大的,那麼在這種情況下我們要做的就應該是避免產生迴流和重繪的操作.
如何避免,首先我們要看一下,都有那些操作可能會引發迴流和重繪:

引發迴流的操作

  • 改變 DOM 的幾何屬性
  • 改變 DOM 樹的結構(即改變元素在文檔流中的位置)
  • 獲取一些特定的屬性值.如 offsetTop、offsetLeft、 offsetWidth、offsetHeight 等.

這裏額外提一下,瀏覽器在迴流操作這裏做了優化,大意就是維護了一個更新隊列,這個更新隊列在一定時間後或者當我們獲取元素的幾何位置屬性時會被清空,執行 DOM 操作.所以我們要避免頻繁的讀寫元素的幾何位置屬性,以免發生佈局抖動.

引發重繪的操作

當我們修改元素的像素信息是都會引發重繪的操作,如修改 color,backgroundColor 等屬性時.

如何避免迴流與重繪

  • 避免逐條修改樣式,應該使用類名合併樣式修改
  • 將要修改的 DOM 進行文檔流的剝離,我們可以將我們要連續修改多次的 DOM 進行 display:none 屬性的修改,這樣雖然會造成迴流操作,但是避免了多次修改的情況下造成的佈局抖動
  • 直接越過迴流和重繪,將元素提升爲合成層,該方法可以參考一下上面講到的GPU 加速部分.
  • 巧妙的使用Micro-Task,微任務會在瀏覽器的下一幀刷新之前執行避免了多次瀏覽器刷新操作

總結

介紹到這裏,大致理清了瀏覽器渲染的一個大致流程,我們也可以順着這個流程去進行一些渲染層面的性能優化.因爲篇幅的關係,沒有辦法詳細展開,所以只能是提供一些渲染優化的思路.
這也僅僅是起到拋磚引玉的作用,希望看到這裏的大神,如果有興趣的話能提出自己的建議或者意見.幫助本渣彌補知識上的漏洞,不勝感激.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章