淺析渲染引擎與前端優化

Reference: http://blog.csdn.net/john1337/article/details/53579506css

 

本文以 Chrome 瀏覽器的內核 WebKit(更確切是 WebKit 分支 Blink,如下統稱爲 WebKit )爲例,對渲染引擎如何展現頁面作個簡單、全面的瞭解。html

 

本文主要是兩方面內容:前端

  • 淺析瀏覽器內核的工做原理(以 WebKit 2 爲例)。css3

  • 淺析由瀏覽器內核想到的前端優化,或者說前端優化規則是從哪兒來的。git

你們知道,大部分的 WEB 頁面依託瀏覽器呈現,而瀏覽器可以將頁面展現出來,基本依賴於瀏覽器的內核,即渲染引擎。今天以 Chrome 瀏覽器的內核 WebKit(更確切是 WebKit 分支 Blink,如下統稱爲 WebKit )爲例,對渲染引擎如何展現頁面作個簡單、全面的瞭解。github

瀏覽器的渲染引擎及其依賴模塊算法

渲染引擎主要是將 WEB 資源如 HTML、CSS、圖片、JavaScript等通過一系列加工,最終呈現出展現的圖像。渲染引擎主要包含了對這些資源解析的處理器,如 HTML 解釋器、CSS 解釋器、佈局計算+繪圖工具、JavaScript 引擎等。爲了更好地呈現渲染效果,渲染引擎還會依賴網絡棧、緩存機制、繪圖工具、硬件加速機制等。瀏覽器

瀏覽器的渲染過程緩存

瀏覽器的渲染過程,主要包括兩大部分:網頁資源加載過程渲染過程安全

上圖將整個網頁渲染的過程作了大體的剖析。如下咱們按照數據流向,逐一詳細剖析每一個過程。

1、域名解析 DNS

當咱們在瀏覽器中輸入 URL 後,瀏覽器首先會進行域名解析。通常狀況下,一次 DNS 域名解析大概須要 60-120 ms,一次 TCP 的三次握手須要 1.5 個 RTT(round-trip time)。WebKit 的方案是 採用 DNS 預取技術和 TCP 預鏈接技術。

DNS 預取技術利用現有 DNS 機制,提早解析網頁中可能的網絡鏈接。即對用戶瀏覽網頁中存在的連接,用較少的 CPU 和網絡帶寬來解析這些連接的域名或 IP 地址;等用戶單擊連接時,就會節省時間~ 特別是域名解析慢的時候~

一樣,在地址欄輸入連接時,候選項也會被默默地執行 DNS 預取~。在 DNS 預取後,會預先創建 TCP 鏈接。

對此前端優化建議:

  • 在頁面中指定預取域名:<link rel=」dns-prefetch」 href=」http://this-is-a.com」>

  • 大數據分析,推測用戶可能點擊的連接,提早預取。

  • 減小頁面中的域名數量,能夠直接減小DNS的請求。

2、SPDY 和 HTTP2

由於請求帶來的 TCP 三次握手的 1.5 RTT 延遲,Google 引入 SPDY,嘗試解決HTTP的延遲和安全性(HTTP 明文方式)問題。不過,SPDY 促使了 HTTP2.0 的誕生後,本身也再也不更新,逐步退出。

SPDY 基於 SSL 之上,輕鬆兼容 HTTP 新老版本。其優點以下:

  • 多路複用。一個 TCP 鏈接傳輸多個資源。減小 TCP 鏈接成本。

  • 不一樣資源,不一樣優先級。好比優先加載首屏。

  • Header 頭壓縮。減小傳送的字節數。SPDY 對 Header 壓縮率可高達 80%。

SPDY 開拓了 HTTP 新局面,秒殺咱們太多的前端優化工做,從本質上提高了頁面加載速度。但咱們前端優化的工做仍是不能偏廢。向着繼續減小請求減小 TCP 鏈接創建的路上,讓咱們繼續。

  • 合併資源,如 combo 合併 JavaScript 文件、CSS 文件,利用 sprite 合併圖片,圖片地圖等;

  • 當頁面資源較小時,可直接放頁面中,如小圖可以使用 Base64 編碼格式引入。甚至一些基礎樣式,或首屏依賴樣式,均可以放在頁面中;

  • 資源壓縮技術。如 Gzip 等。主要是對響應數據的壓縮~

  • 精簡 JavaScript 和 CSS 代碼。減小無用的空格。壓縮混淆~

  • 避免連接重定向、避免錯誤的連接請求。創建屢次連接、屢次 DNS 解析,阻礙 DNS 預取技術。及時更新掉你頁面中沒有價值的連接吧。

3、資源加載

域名解析完,TCP 鏈接也創建起來後,資源加載器就開始工做了。

資源及資源加載器

資源包括:HTML、JavaScript、CSS 樣式表、圖片、SVG、字體文件、視頻音頻等。資源加載器有三種:

  1. 特定加載器,只加載某一種。如ImageLoader類。

  2. 緩存機制的資源加載器。特定加載器經過它查找是否有緩存資源,屬於 HTML 的文檔對象。

  3. 通用的資源加載器。在WebKit須要從網絡或文件系統獲取資源時使用。只負責獲取資源的數據,被全部特定資源加載器共享。

資源加載的過程

在 WebKit 中,資源都以 CachedResource 爲基類,以 Cached 爲前綴,體現了瀏覽器的緩存機制。即請求資源時,瀏覽器會先看緩存中有沒有這個資源,而後再決定是否向服務器發出請求。

這引出兩個問題,首先,緩存資源的生命週期

瀏覽器緩存不會無限增大,緩存池中的數據必然出現更替,WebKit 採用 LRU 最近最少使用算法更新緩存池數據。WebKit 遵循 HTTP 協議,當頁面刷新時,判斷資源是否在資源池。若存在,則附上該資源在本地的一些信息(如修改時間等),發送 HTTP 請求給服務器,服務器根據信息做出判斷,若資源沒更新則網絡狀態爲 304,利用現有資源;不然執行資源加載過程。

其次,資源加載過程

資源池中沒有該資源時,執行加載過程。WebKit 能夠並行(多線程)下載普通資源和 JavaScript 資源。在當前主線程被阻塞時,WebKit 會啓動另外一個線程去遍歷後邊的網頁,收集須要的資源 URL再發請求,避免阻塞。

基於資源加載,前端優化建議:

利用緩存機制,緩存經常使用且短時期內不會變動的資源,或給資源設置過時時間。

好比設置 ETag/Last-Modified 和 Expires/Cache-Control
Expires/Cache-Control 二者做用一致,指明資源有效期,若是本地緩存還在有效期內,瀏覽器直接使用本地緩存,再也不發送請求。二者同時配置時,Cache-Control 高於 Expires。

配置 ETag/Last-Modified 後,瀏覽器再次訪問 URL 時,還會向服務器發送請求,確認文件是否已修改,沒修改則服務器返回304,瀏覽器直接從本地緩存獲取數據;修改過則服務器返回數據給瀏覽器。二者同時配置,服務器會優先檢測 ETag,一致纔會繼續檢測 Last-Modified。二者同時配置,可使服務器更準確的判斷瀏覽器是否已有須要的緩存數據。

ETag/Last-Modified 和 Expires/Cache-Control 兩對都設置時, Expires/Cache-Control 優先級更高。因此,只要本地緩存在有效期內,就不會發送請求。但頁面 F5 刷新和強刷時,緩存將失效。

鑑於資源下載中可能被阻塞,將 JavaScript 文件放置頁面下方。JavaScript 資源就是阻塞主線程的那個,而重建一個線程也是須要時間滴,因此把 JavaScript 扔最後吧~ 但 JavaScript 資源並不影響以前資源的加載和 DOM 樹的構建。

4、從 URL 到 DOM 樹的構建

當咱們拿到頁面所需的資源後,渲染引擎便啓動 HTML 解釋器,對獲取的資源進行解析處理。網頁代碼(字節流)通過詞法分析器解碼,再由語法分析器解釋成詞語 Token,並構建成節點 Node,直到最終構建成一棵 DOM 樹。

期間,當節點爲 JavaScript 節點時,將啓動 JavaScript 引擎,這時將阻塞 DOM 樹的構建。由於 JavaScript 執行過程當中, JavaScript 極可能會對 DOM 樹進行讀寫操做。直到 JavaScript 執行完畢, DOM 樹纔會恢復構建。

其餘資源並不影響 DOM 樹的構建。

前端優化中,建議將 CSS 文件放在頁首,以便構建 DOM 樹;而將 JavaScript 文件儘可能放在頁面下方,防止阻塞構建 DOM 樹;而 JavaScript 的 onload 事件裏,不要寫太多影響首屏渲染的、操做 DOM 樹的 JavaScript 代碼。

另外強調一下:

DOMContentLoaded: DOM 樹構建完;
DOM 的onload事件: DOM 樹構建完且網頁依賴的資源都加載完了~

5、網頁排版過程:由 DOM 樹到構建 RenderLayer 樹

這一過程,就像是頁面的排版過程。它經過 CSS 樣式信息,對 DOM 樹進行排版,造成 RenderObject 樹及 RenderLayer 樹。

在 DOM 樹構建完成後,WebKit 爲 DOM 樹節點構建 RenderObject 對象。WebKit 將根據盒模型計算節點的位置、大小等樣式信息(即佈局計算或排版),並將這些信息保存到對應的 RenderObject 對象。

1. CSS解釋器

CSS解釋過程,是從 CSS 字符串通過 CSS 解釋器(CSSParser、CSSGrammer)處理後,變成渲染引擎的內部樣式規則表示的過程。樣式規則是解釋器的輸出結構,是樣式匹配的輸入數據。

具體過程:WebKit 在渲染元素時,CSS 解釋器獲取樣式信息,返回匹配好的結果樣式信息。每一個元素可能須要匹配不一樣來源的規則,依次是用戶代理(瀏覽器)規則集合、用戶規則集合和HTML頁面中包含的自定義規則集合。三者匹配方式相似。

對於每一個規則集合,先查找 ID 規則,檢查有無匹配的規則,而後依次檢查類型規則、標籤規則等。匹配好的規則,保存到匹配結果中。WebKit 對這些規則進行排序。對於元素須要的樣式屬性,WebKit 選擇從高優先級規則中選取,並將樣式屬性值返回。

2. 渲染基礎:RenderObject 樹

DOM 樹通過佈局計算、CSS parse 後,將樣式信息存儲在 RenderObject 對象中,並構建成 RenderObject 樹。同時,WebKit 會根據網頁的層次結構建立 RenderLayer 樹,完成繪圖上下文。DOM 樹、Render 樹和繪圖上下文同時並存,直到頁面銷燬。

RenderObject 樹,基於 DOM 樹的一棵新樹,是佈局計算和渲染等機制的基礎設施。

DOM 節點創建新的 RenderObject 對象的時機:

  • DOM 樹的 Document 節點。

  • DOM 樹的可視節點,如html、body、div 等。非可視節點如meta、head、script 等不建立。

  • 爲知足 WebKit 處理,須要創建匿名 RenderObject 節點,它不對應於 DOM 樹的任何節點。如:匿名的 RenderBlock 節點。

DOM 樹的每一個節點對象會遞歸檢查是否須要建立 RenderObject,並根據 DOM 節點類型建立 RenderObject 節點;動態加入的 DOM 元素,會相應的建立 RenderObject 節點。全部這些節點構成一棵 RenderObject 樹。

3. 渲染基礎:網頁層次和 RenderLayer 樹

在 HTML 頁面上,網頁分層展現。目的有兩個:1. 方便開發網頁、設置網頁的層次;2. 簡化 WebKit 渲染的邏輯。

在RenderObject 樹基礎上,WebKit 根據須要爲其中的某些節點建立新的 RenderLayer 節點,並造成一棵 RenderLayer 樹。

RenderObject 節點創建新 RenderLayer 對象的時機:

  • DOM 樹的 Document 節點對應的 RenderView 節點。

  • DOM 樹的 Document 的子節點,即 HTML 節點對應的 RenderBlock 節點。

  • 顯式的指定 CSS 位置的 RenderObject 節點。

  • 有透明效果的 RenderObject 節點。

  • 節點有溢出 overflow、alpha 或反射等效果的 RenderObject 節點。

  • 使用Canvas 2D、3D (WebGL)技術的 RenderObject 節點。

  • Video 節點對應的 RenderObject 節點。

RenderLayer 節點的使用能夠有效減小網頁結構的複雜程度,並在許多狀況下能減小從新渲染的開銷。

4. 佈局計算及重繪時機

CSS 盒模型,是佈局計算的基礎;渲染引擎用來肯定如何排版元素、及元素間的位置關係。

佈局計算,是針對 RenderObject 樹及其子樹的計算,是一種遞歸計算,其節點信息須要先計算其子節點的位置、大小等信息。RenderObject 對象會將計算結果存儲,等待渲染時機。

  • 每一個元素會實現本身的 layout。

  • 頁面元素定義了寬高,則按自定義寬高肯定元素大小。

  • 文本節點等內聯元素,須要結合字號大小、文字多少肯定寬高。

  • 頁面元素肯定的寬高超過了佈局容器包含塊提供的寬高,同時 overflow 爲 visible 或 auto,WebKit 則提供滾動條保證可顯示全部內容。

  • 通常頁面元素的寬高是在佈局時經過計算得來。除非網頁定義了頁面元素的寬高。

重繪時機:只要樣式發生變化,就從新計算。

  • 首次打開頁面,瀏覽器設置網頁的可視區域,並調用計算佈局的方法。可視區域改變時,網頁包含塊的大小也會改變,WebKit 須要從新計算佈局。

  • 網頁的動畫會觸發佈局計算。動畫可能改變樣式屬性。

  • JavaScript 經過 CSSOM(CSS 對象模型) 直接修改樣式,會觸發 WebKit 從新計算佈局。

  • 用戶交互,如滾動網頁。

前端優化建議,因佈局計算耗時間,一旦佈局發生變化,WebKit 就須要後面的從新繪製操做。SO,減小樣式的變更~減小重繪~利用 CSS3 新功能(如 CSS3 變形 translate、scale、rotate 等方法,過渡 transition 方法等)可有效提升網頁的渲染效率。

6、 網頁渲染過程:由 RenderLayer 樹到最終的圖像

在上一個過程,網頁完成了 DOM 樹到 RenderLayer 樹的佈局計算和排版處理。接下來,由渲染引擎(通常是繪圖類工具)完成對 RenderLayer 樹的繪製,並最終造成圖像,展現給用戶。

1. 繪圖上下文

繪圖上下文,全部的繪圖操做都是在該上下文中進行的。它是一個與平臺無關的抽象類,它將每一個繪圖操做橋接到不一樣的繪圖具體實現類。

2D 繪圖上下文:

  • 提供基本繪圖單元的繪製接口及設置繪圖的樣式。

  • 繪圖接口包括:畫點、畫線、畫圖、畫多邊形、畫文字等。繪圖樣式包括顏色、線寬、字號、漸變等。

  • CPU 來完成 2D 操做。或用 3D 圖形接口( OpenGL )完成。

3D 繪圖上下文:支持 CSS3D、WebGL 等。

  • 使用 3D 圖形接口(OpenGL、Direct3D 等)

2. 渲染方式

軟件渲染:CPU。一般渲染的結果是一個位圖,繪製每一層時都使用該位圖,區別在於位置可能不一樣,每一層按從後到前的順序。不必爲每層分配一個位圖,不必合成。

缺點:對 HTML5 新技術,

  • 能力不足,CSS3D、WebGL;

  • 性能很差,如視頻、Canvas 2D;

  • 使用率降低,特別是移動端。

優點:對更新區域處理,軟件渲染可能只須要計算極小區域,硬件則須要繪製其中一層或多層,再合成。硬件代價大。

硬件加速渲染:GPU 必須有合成的步驟。分層繪製+合成。不過對於更新區域,若是隻是在一個層,硬件可能會更快。

WebKit 的實現方式:

  • 使用合適的網頁分層技術、減小從新計算的佈局和繪圖。

  • 使用CSS 3D 變形和動畫技術。CSS 3D 變形技術,能讓瀏覽器僅使用合成器合成全部層就能夠達到動畫效果。不須要佈局計算和重繪~

前端優化建議:

  • 減小重繪:由於重繪是要計算佈局、繪圖、合成三個階段。其中計算佈局和繪圖比較費時,合成要少

7、總結

至此,從輸入 URL 到頁面呈現,咱們大體作了介紹。但這只是皮毛最上方的一點,更多瀏覽器內核的實質,值得咱們下載一份源碼,編譯解析深挖~ 相信在前端優化的路上,知其然,知其因此然~ 定會走得跟遠~~

相關文章
相關標籤/搜索