1. 程式人生 > >瀏覽器內核、引擎、頁面呈現原理及其優化

瀏覽器內核、引擎、頁面呈現原理及其優化

應用 事情 大致 們的 成功 規則 變化 節點 時有

瀏覽器內核、引擎、頁面呈現原理及其優化

介紹瀏覽器內核、JavaScript 引擎以及頁面呈現原理等基礎知識,同時根據原理提出頁面呈現優化方案。

瀏覽器內核

瀏覽器內核又叫渲染引擎,主要負責 HTML、CSS 的解析,頁面布局、渲染與復合層合成。瀏覽器內核的不同帶來的主要問題是對 CSS 的支持度與屬性表現差異。

現在主流的內核有:Blink、Webkit、Gecko、EdgeHTML、Trident,這裏面有幾個需要註意的地方:

  1. Blink 是在 Webkit 的基礎上的改進,是現在對新特性支持度最好的內核

  2. 移動端基本上全部是 Webkit 或 Blink 內核(除去 Android 上騰訊家的 X5),這兩個內核對新特性的支持度較高,所以新特性可以在移動端大展身手。

  3. Trident 是 IE4+ 的內核,一直持續到 IE11,EdgeHTML 是微軟拋棄 IE 後開發的全新內核

  4. 更多資料請看附錄表格

幾種渲染模式

一般現代瀏覽器都會有以下幾種渲染模式:

  1. 標準模式
  2. 接近標準模式(又稱有限怪異模式)
  3. 怪異模式

不同渲染模式出現的原因

在 IE5 與 NS4 那個年代,瀏覽器大戰,標準未立,Web 則在經歷早期快速地發展。

後來標準逐步建立,新標準的規範與以前 IE5、NS4 的實現存在著不可避免的差異差異,但是此時的網絡世界許許多多舊時的頁面正在運行,如果按照新標準的實現來渲染的話會有大量的問題出現。

所以此時大部分現代瀏覽器廠商想到了區別性地使用不同渲染模式

來對待這些 Web 頁面。

而 IE 隨著升級,對現代標準的支持也越來越完善,所以 IE 為了正常渲染舊時頁面,支持我們指定哪個版本的 IE 模式來渲染頁面

總結就是:

  1. 怪異模式是 IE5 與 NS4 年代遺留問題的回退方案
  2. 怪異模式在大部分瀏覽器都有部署,並且能在一定的機制下觸發

不同渲染模式的觸發

標準未立之前,HTML 文檔是沒有文檔頭的,同時在 HTML5 之前的 HTML4/3 的文檔頭都有各自的特征,所以在大部分現代瀏覽器下觸發的機制如下:

  1. DOCTYPE 頭觸發怪異模式

  2. DOCTYPE 頭不正確(不是 html)也觸發怪異模式

    如:<!DOCTYPE svg>

  3. DOCTYPE 頭為 HTML3 頭觸發怪異模式

  4. DOCTYPE 頭為 HTML4 頭則觸發接近標準模式(或稱有限怪異模式

  5. 常見的 HTML5 DOCTYPE 聲明則使用標準模式

在 IE 下,除了文檔頭的差異可以自動觸發渲染模式的選擇,我們還能手動指定(在 IE8+ 適用)使用哪個版本的 IE 渲染模式來渲染我們的頁面(擴展閱讀):

1
2
3
4
5
6
7
8
9
<!-- 使用當前操作系統已裝的最新的 IE -->
<!-- chrome=1 是針對雙核瀏覽器使優先使用 Chrome -->
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1">

<!-- 使用 IE9 -->
<meta http-equiv="x-ua-compatible" content="ie=9">

<!-- 如果你需要使用 IE5 的怪異模式 -->
<meta http-equiv="x-ua-compatible" content="ie=5">

不同渲染模式的表現差異

怪異模式與標準模式

  1. 怪異模式使用不同於標準的盒模型(也就相當於 IE8+ 下的:box-sizing: border-box
  2. 怪異模式下某些行內(inline)元素的垂直對齊行為表現怪異:怪異模式下對齊圖片至包含它們的盒子的下邊框,而 標準模式圖片對其到父盒的 baseline

接近標準模式(有限怪異模式)與標準模式

主要區別即為上面的第 2 點

JavaScript 引擎

JavaScript 負責 JavaScript 代碼的解釋與執行,主流的 JavaScript 引擎有:V8、SpiderMonkey、JavaScriptCore、Chakra。

瀏覽器與引擎詳情見附錄表格。

頁面呈現原理

當我們點擊一個鏈接,服務器將 HTML 代碼傳輸到我們的瀏覽器,瀏覽器在接收到這份 HTML 代碼之後是如何一步步將頁面呈現出來的呢?這裏面瀏覽器需要做哪些工作?如何優化呈現的過程提升 Web 應用質量?

六部曲

一個頁面的呈現,粗略的說會經過以下這些步驟:

  1. DOM 樹的構建(Parse HTML)

  2. 構建 CSSOM 樹(Recaculate Style)

    為什麽是 Re-caculate Style 呢?這是因為瀏覽器本身有 User Agent StyleSheet,所以最終的樣式是我們的樣式代碼樣式與用戶代理默認樣式覆蓋/重新計算得到的。

  3. 合並 DOM 樹與 CSSOM 樹為 Render 樹

  4. 布局(Layout)

  5. 繪制(Paint)

  6. 復合圖層化(Composite)

    圖層化是自己理解後形象的意譯

其中布局(Layout)環節主要負責各元素尺寸、位置的計算,繪制(Paint)環節則是繪制頁面像素信息,合成(Composite)環節是多個復合層的合成,最終合成的頁面被用戶看到。

六部曲中的阻塞

雖然六部曲看似和諧,分工合作,有序進行。但是實際上這裏面卻是波雲詭譎,風起雲湧,就像平時的工作一樣,看似你和我各司其職,分工明確,但是實際幹起活來卻可能因為某一個人的某一環而阻滯整個進度。

我們來分析這六部曲中存在的阻塞問題:

  1. 當遇到 JavaScript 腳本或者外部 JavaScript 代碼時,瀏覽器便停止 DOM 的構建(阻塞 1

    那是否停下 DOM 的構建的同時,立馬就執行 JavaScript 代碼或者下載外部腳本執行,其實還是要視情況而定,見 2

  2. 當遇到 <script> 標簽需要執行腳本代碼時,瀏覽器會檢查是否這個 <script> 標簽以上的 CSS 文件是否已經加載並用於構建了 CSSOM,如果 <script> 上部還有 CSS 樣式沒加載,則瀏覽器會等待 <script> 上方樣式的加載完成才會執行該 <script> 內的腳本(阻塞 2

  3. DOM 樹與 CSSOM 樹的成功構建是後面步驟的根基(同步阻塞

  4. 同時外部腳本、外部樣式表的下載也是耗費時間較多的點

六部曲之 DOM 樹的構建

技術分享圖片
瀏覽器構建 DOM 樹可以簡單的總結為以下幾步:

  1. 轉碼(Bytes -> Characters)—— 讀取接收到的 HTML 二進制數據,按指定編碼格式將字節轉換為 HTML 字符串

  2. Tokens 化(Characters -> Tokens)—— 解析 HTML,將 HTML 字符串轉換為結構清晰的 Tokens,每個 Token 都有特殊的含義同時有自己的一套規則

  3. 構建 Nodes(Tokens -> Nodes)—— 每個 Node 都添加特定的屬性(或屬性訪問器),通過指針能夠確定 Node 的父、子、兄弟關系和所屬 treeScope(例如:iframe 的 treeScope 與外層頁面的 treeScope 不同)
    技術分享圖片

  4. 構建 DOM 樹(Nodes -> DOM Tree)—— 最重要的工作是建立起每個結點的父子兄弟關系

在 Chrome 開發者工具下 Timeline 面板的 Parse HTML 階段對應著 DOM 樹的構建

擴展閱讀:從Chrome源碼看瀏覽器如何構建DOM樹

留意這篇文章的這些點:

  1. DOM 構建時對 DOCType 處理
  2. DOCType 的不同或漏缺帶來的文檔解析模式(怪異模式、有限怪異模式、標準模式)的影響
  3. 處理開標簽與閉標簽的壓棧、彈棧處理
  4. Chromium 對待自定義標簽的處理
  5. JavaScript 方法查找 DOM 的過程,使用 ID、類名、復雜選擇器查找 DOM 的對比

六部曲之 CSSOM 樹的構建

CSSOM 樹的構建 “原料” 的來源有:外部 CSS 文件、內部樣式、內聯樣式

CSSOM 樹的構建其實是一個 樣式的重新計算 的過程,為什麽是重新計算呢?

用戶代理(即瀏覽器)本身有一套內置樣式表,所以我們最終的 CSSOM 樹其實是用戶代理樣式與頁面所有樣式的重新計算

所以在 Chrome 瀏覽器開發者工具的 Timeline 面板下,CSSOM 樹的構建對應的是 Recalculate Style 階段

與 DOM 樹的構建過程相似,CSSOM 的構建也要經歷以下過程:
技術分享圖片

最終構建的 CSSOM 樹大致如下:
技術分享圖片

六部曲之渲染樹的構建

  1. DOM 樹與 CSSOM 樹融合成渲染樹

  2. 渲染樹只包括渲染頁面需要的節點

    排除 <script> <meta> 等功能化、非視覺節點
    排除 display: none 的節點

技術分享圖片

六部曲之布局

Layout 階段做的工作:確定頁面各元素的位置、尺寸。

Layout 在 Chrome 開發者工具 Timeline 面板中被歸並到 Paint 階段

當元素某些樣式變更/JavaScript 執行某些樣式請求,會導致 Layout trashing,又叫做回流(Reflow)。

六部曲之繪制

一旦布局(Layout)步驟完成,瀏覽器便觸發 “Paint Setup” 與 “Paint” 事件(渲染引擎底層概念),執行 paint 操作,結合渲染樹與布局信息繪制實際像素

註:在 Timeline 工具內,LayoutPaint 兩個過程被統一歸並到 Paint 階段。

六部曲之復合圖層化

在很多情況下,我們不會將復合圖層化歸入頁面呈現的必要過程。圖層化是瀏覽器為了充分利用已有渲染成果(緩存渲染成果),最小化 GPU 運算,將“臟區”提升為復合圖層,隔離變化影響的操作。

見 鏈接

頁面性能優化

知道了頁面渲染的原理,那麽我們也就得到了頁面性能優化的依據。提煉六部曲中每一步的優化空間,針對六部曲中的每一步提出針對性的優化方案也就能達到我們最終的優化目的。

優化不可避免的阻塞:優化關鍵呈現路徑

關鍵呈現路徑裏的一些概念

  • 關鍵資源:可能阻止網頁首次呈現的資源。
  • 關鍵路徑長度:即往返過程數量,或提取所有關鍵資源所需的總時間。
  • 關鍵字節:實現網頁首次呈現所需的總字節數,是所有關鍵資源的傳輸文件大小總和。 帶有一個 HTML 網頁的首個示例包含一項關鍵資源(HTML 文檔),關鍵路徑長度也與 1 個網絡往返過程(假設文件較小)相等,而且總的關鍵字節數正好是 HTML 文檔本身的傳輸大小。

優化關鍵呈現路徑的指導原則

  • 盡量減少關鍵資源數量。
  • 盡量減少關鍵字節數。
  • 盡量縮短關鍵路徑的長度。

優化關鍵呈現路徑常規步驟

  1. 分析和描述關鍵路徑:資源數量、字節數、長度。
  2. 盡量減少關鍵資源數量:刪除相應資源、延遲下載、標記為異步資源等等。
  3. 優化剩余關鍵資源的加載順序:你需要盡早下載所有關鍵資源,以縮短關鍵路徑長度。
  4. 盡量減少關鍵字節數,以縮短下載時間(和往返次數)。

優化關鍵呈現路徑的具體建議

  1. 文件合並、壓縮

  2. 推薦使用異步(async) JavaScript 資源,或使用延遲(defer)執行的 JavaScript

  3. 一般 <script> 腳本的靠後書寫

  4. 避免運行時間長的 JavaScript,耗時任務的拆分,chunk 化運行

    例如:使用定時器將大任務拆分為小任務,使得瀏覽器得到空隙做其他事情。

  5. 避免使用 CSS import

  6. 內聯、內部化阻止呈現的 CSS

    一般不采用,百度、Google 這樣的極度重視性能與體驗的服務才可能這樣做。

針對復合圖層化的優化

因為瀏覽器有圖層化這個機制,那麽我們就搞懂它並充分利用吧。

復合圖層化機制

某些屬性的變更(transformopacity)滿足以下條件:

  • 不影響文檔流。
  • 不依賴文檔流。
  • 不會造成重繪。

那麽這些屬性變更時就需要一種機制:機制需要能將屬性變更的部分與頁面其他部分隔離開來,其他部分已經渲染完好進行緩存,變更的部分在單獨的圖層上進行,然後對緩存的部分與變更的圖層進行合成。

所以圖層化的關鍵字:緩存隔離圖層合成

使用 transformopacity 進行屬性變更是經典的符合圖層化方法,以下是其他會提升元素為復合圖層的場景

  1. 3d 或透視變換 CSS 屬性,例如 translate3d, translateZ 等等(JS 一般通過這種方式,使元素獲得復合圖層)
  2. <video> <iframe> <canvas> <webgl> 等元素。
  3. 混合插件(如flash)。
  4. 元素自身的 opacitytransform 做 CSS 動畫。
  5. 擁有 CSS Filter 的元素。
  6. 使用 will-change 屬性。
  7. position:fixed
  8. 元素有一個 z-index 較低且包含一個復合層的兄弟元素(換句話說就是該元素在復合層上方)

圖層化的優勢

很容易看出來:充分利用緩存、隔離的思想,無需像回流、重繪那麽大性能(GPU、CPU)開支,圖層化能帶來動畫性能的提升。

圖層化的潛在問題 —— 內存開銷

那麽圖層化的弊端在哪裏呢?

因為圖層化的存在,每個圖層對需要在內存中存儲該圖層相關的信息,當圖層太多會造成內存開銷過大的情況(如下圖)。

技術分享圖片

同樣表現的頁面,單圖層與多圖層的內存開銷

因為開銷,所以節制

內存開銷在桌面端可能還能接受,但是在資源有限的移動端,復合圖層過多便可能導致內存開支過大,頁面反而變得停滯、卡頓,甚至瀏覽器假死,系統無法正常運行。

針對回流的優化

  1. CSS Triggers

    1. CSS3 > JavaScript
    2. 屬性變更優先考慮順序(性能表現排序)
      1. transfrom, opacity
      2. background-color
      3. position - top bottom left right
      4. width height
      5. margin, padding, border
  2. What forces layout

    JavaScript 存在這樣的機制:當連續有大量 DOM 樣式的操作時,出於性能考慮,防止零碎變更導致頻繁的回流、重繪,會盡可能地將這些操作先緩存起來,然後一次性地變更。這個機制我們難以察覺但是確實存在。

    然而當我們進行某些 DOM 樣式的讀、寫時,出於時效性的考慮,則會立即觸發瀏覽器回流、重繪以返回正確、合理的值。

其他優化技巧

節流函數

已經比較明白了,那就略吧 參考

惰性載入函數

也已經比較明白,也略吧 參考

重任務分片多幀

實例講解

Timeline 工具

使用 Timeline 工具我們能做以下事:

  1. 頁面渲染幀率分析,得到異常幀信息
  2. 各類文件執行耗時/耗資源分析
  3. 文件等待 - 下載時間
  4. 頁面呈現期間的事件列表
  5. 某個幀/某個事件的詳細信息分析

技術分享圖片

理解 Timeline 工具使用,讀懂這圖就夠了

技術分享圖片

常用事件,更多事件見[擴展閱讀](#擴展閱讀)

而我們在日常開發中,用 Timeline 最多的場景是:

  1. 編寫動畫,Debug 不流暢的異常幀,針對性優化(見本章擴展閱讀一節)
  2. 偵測 重新計算樣式重新布局重繪重新合成 等事件,針對性優化
  3. 了解 JavaScript 函數調用棧以及函數調用帶來的回流/重繪事件信息
  4. 查看在某個事件中代碼的執行耗時(點擊上圖 Main 部分,點擊下方常駐 Panel 內與文件相關的可點擊鏈接)
    技術分享圖片

讓你的 Timeline 調試更強大

  1. 選擇性地開啟以下開關
    技術分享圖片

  2. 開啟開發者工具實驗性特性開關
    技術分享圖片

    技術分享圖片

擴展閱讀

Timeline 官方簡介
Timeline 事件參考
推薦:Timeline 進行幀分析,避免頁面卡頓

附錄

瀏覽器內核與 JS 引擎一覽

瀏覽器/RunTime內核(渲染引擎)JavaScript 引擎
Chrome Blink(28~)
Webkit(Chrome 27)
V8
FireFox Gecko SpiderMonkey
Safari Webkit JavaScriptCore
Edge EdgeHTML Chakra(for JavaScript)
IE Trident Chakra(for JScript)
PhantomJS Webkit JavaScriptCore
Node.js - V8

瀏覽器內核、引擎、頁面呈現原理及其優化