一個網址引起的操做 -- 瀏覽器「內核」

GitHub連接:https://github.com/zwwill/blo...css

前言

四不四常常有人在你面前念(zhūang)叨(bī),「這是瀏覽器內核的問題!Safari[səˈfɑri]的內核不支持!」?html

今天我們就來聊聊所謂的「內核」!前端

要講內核首先要講瀏覽器基礎,瀏覽器基礎是前端知識網中的一個小分支,也是前端開發人員必須掌握的基礎知識點。他貫穿着前端的整個網絡體系,項目優化也是圍繞着瀏覽器進行的。git

一個網址引起的操做

開發人員在面試的時候或許會被問到:程序員

從你在瀏覽器輸入一個網址到網頁內容徹底被展現的這段時間內,都發生了什麼事情?github

確實是個老生常談的問題,但問題的答案並非惟一的,或許在三五年前,這個問題還會有一個「相對」標準的答案。web

  1. 瀏覽器在接收到這個指令時,會開啓一個單獨的線程來處理這個指令,首先要判斷用戶輸入的是否爲合法或合理的 URL 地址,是否爲 HTTP 協議請求,若是是那就進入下一步面試

  2. 瀏覽器的瀏覽器引擎將對此 URL 進行分析,若是存在緩存「cache-control」且未過時,則會從本地緩存提取文件(From Memory Cache,200返回碼),若是緩存「cache-control」不存在或過時,瀏覽器將發起遠程請求ajax

  3. 經過 DNS 解析域名獲取該網站地址對應的 IP 地址,連同瀏覽器的 Cookie、 userAgent 等信息向此 IP 發出 GET 請求。數據庫

  4. 接下來就是經典的「三次握手」,HTTP 協議會話,瀏覽器客戶端向 Web 服務器發送報文,進行通信和數據傳輸。

  5. 進入網站的後端服務,如 Tomcat、Apache 等,還有近幾年流行的 Node.js 服務器,這些服務器上部署着應用代碼,語言有不少,如 Java、 PHP、 C++、 C# 和 Javascript 等。

  6. 服務器根據 URL 執行相應的後端應用邏輯,期間會使用到「服務器緩存」或「數據庫」。

  7. 服務器處理請求並返回響應報文,若是瀏覽器訪問過該頁面,緩存上有對應資源,與服務器最後修改記錄對比,一致則返回 304,不然返回 200 和對應的內容。

  8. 瀏覽器接收到返回信息並開始下載該 HTML文件(無緩存、200返回碼)或從本地緩存提取文件(有緩存、304返回碼)

  9. 瀏覽器的渲染引擎在拿到 HTML 文件後,便開始解析構建 DOM 樹,並根據 HTML 中的標記請求下載指定的 MIME 類型文件(如 CSS、 JavaScript 腳本等),同時使用&設置緩存等內容。

  10. 渲染引擎根據 CSS 樣式規則將 DOM 樹擴充爲渲染樹,而後進行重排、重繪。

  11. 若是含有 JS 文件將會執行,進行 Dom 操做、緩存讀存、事件綁定等操做。最終頁面將被展現在瀏覽器上。

此答案精簡的歸納了「後端爲主的 MVC 模式」及早期 Web 應用的瀏覽器響應的全過程。前端技術發展到如今,「先後端分離」「中間件直出」和「MNV*模式」也已問世,再談及此問題,答案會有所不一樣。

就以「先後端分離」爲例,在上方答案的第4步後,緊接着就不會直接進入後端服務器了。而會被 HTTP 和反向代理服務器,如 Ngnix,攔截。

  • 前置步驟一、二、三、4

  • Ngnix 在監聽到 HTTP(80端口)或 HTTPS(443端口)請求,根據 URL 作服務分發,分發(rewrite)到後端服務器或靜態資源服務器,首頁請求基本是分發到靜態服務器,返回一個 HTML 文件

  • 步驟七、八、九、10

  • 執行 JS 腳本,異步 ajax、 fetch 發起 POST、 GET 請求,從新進入 Ngnix 分發,這次分發到後端服務器,步驟五、六、7,而後返回一個 xml 或 json 格式的信息,通常含有 code(返回碼)和 result(依賴信息)

  • js 回調根據返回碼執行不一樣的邏輯,增刪改頁面元素,此時可能會發生重排或重繪。首頁加載結束。

從以上步驟能夠發現,瀏覽器可能會觸發兩次重繪,極易產生「白屏」或「頁面抖動」現象,爲了解決這個問題「中間件直出」的模式應運而生。另外爲了擴充大前端的陣營,吸納 IOS 和 Android,Google 設計了「MNV*模式」,典型表明就是 ReactNative,但此模式已經脫離了瀏覽器的範疇,此處就再也不作擴展。

以上討論的渲染過程當中使用到了較多的瀏覽器功能,如用戶地址欄輸入框、網絡請求、瀏覽器文檔解析、渲染引擎渲染網頁、 JavaScript 引擎執行 js 腳本、客戶端存儲等。 接下來咱們介紹下瀏覽器的基本結構組成。

瀏覽器的結構組成

瀏覽器通常由七個模塊組成,User Interface(用戶界面)、Browser engine(瀏覽器引擎)、Rendering engine(渲染引擎)、Networking(網絡)、JavaScript Interpreter(js解釋器)、UI Backend(UI 後端)、Date Persistence(數據持久化存儲) 以下圖:

瀏覽器的結構組成

  • 用戶界面 -包括地址欄、後退/前進按鈕、書籤目錄等,也就是你所看到的除了頁面顯示窗口以外的其餘部分

  • 瀏覽器引擎 -能夠在用戶界面和渲染引擎之間傳送指令或在客戶端本地緩存中讀寫數據等,是瀏覽器中各個部分之間相互通訊的核心

  • 渲染引擎 -解析DOM文檔和CSS規則並將內容排版到瀏覽器中顯示有樣式的界面,也有人稱之爲排版引擎,咱們常說的瀏覽器內核主要指的就是渲染引擎

  • 網絡 -用來完成網絡調用或資源下載的模塊

  • UI 後端 -用來繪製基本的瀏覽器窗口內控件,如輸入框、按鈕、單選按鈕等,根據瀏覽器不一樣繪製的視覺效果也不一樣,但功能都是同樣的。

  • JS解釋器 -用來解釋執行JS腳本的模塊,如 V8 引擎、JavaScriptCore

  • 數據存儲 -瀏覽器在硬盤中保存 cookie、localStorage等各類數據,可經過瀏覽器引擎提供的API進行調用

做爲前端開發人員,咱們須要重點理解渲染引擎的工做原理,靈活應用數據存儲技術,在實際項目開發中會常常涉及到這兩個部分,尤爲是在作項目性能優化時,理解瀏覽器渲染引擎的工做原理尤其重要。而其餘部分則是由瀏覽器自行管理的,開發者能控制的地方較少。今天咱們就圍繞這兩個重點其中的一個部分「瀏覽器渲染引擎」也就是進行展開,「瀏覽器內核」。

瀏覽器渲染引擎

瀏覽器渲染引擎是由各大瀏覽器廠商依照 W3C 標準自行研發的,也被稱之爲「瀏覽器內核」。

目前,市面上使用的主流瀏覽器內核有5類:Trident、Gecko、Presto、Webkit、Blink。

Trident:俗稱 IE 內核,也被叫作 MSHTML 引擎,目前在使用的瀏覽器有 IE11 -,以及各類國產多核瀏覽器中的IE兼容模塊。另外微軟的 Edge 瀏覽器再也不使用 MSHTML 引擎,而是使用類全新的引擎 EdgeHTML。

Gecko:俗稱 Firefox 內核,Netscape6 開始採用的內核,後來的 Mozilla FireFox(火狐瀏覽器)也採用了該內核,Gecko 的特色是代碼徹底公開,所以,其可開發程度很高,全世界的程序員均可覺得其編寫代碼,增長功能。由於這是個開源內核,所以受到許多人的青睞,Gecko 內核的瀏覽器也不少,這也是 Gecko 內核雖然年輕但市場佔有率可以迅速提升的重要緣由。

Presto:Opera 前內核,爲啥說是前內核呢?由於 Opera12.17 之後便擁抱了 Google Chrome 的 Blink 內核,此內核就沒了寄託

Webkit:Safari 內核,也是 Chrome 內核原型,主要是 Safari 瀏覽器在使用的內核,也是特性上表現較好的瀏覽器內核。也被大量使用在移動端瀏覽器上。

Blink: 由 Google 和 Opera Software 開發,在Chrome(28及日後版本)、Opera(15及日後版本)和Yandex瀏覽器中使用。Blink 實際上是 Webkit 的一個分支,添加了一些優化的新特性,例如跨進程的 iframe,將 DOM 移入 JavaScript 中來提升 JavaScript 對 DOM 的訪問速度等,目前較多的移動端應用內嵌的瀏覽器內核也漸漸開始採用 Blink。

渲染引擎的工做流程

瀏覽器渲染引擎最重要的工做就是將 HTML 和 CSS 文檔解析組合最終渲染到瀏覽器窗口上。以下圖所示,渲染引擎在接受到 HTML 文件後主要進行了如下操做:解析 HTML 構建 DOM 樹 -> 構建渲染樹 -> 渲染樹佈局 -> 渲染樹繪製。

渲染引擎工做流程

解析 HTML 構建 DOM 樹時渲染引擎會將 HTML 文件的便籤元素解析成多個 DOM 元素對象節點,而且將這些節點根據父子關係組成一個樹結構。同時 CSS 文件被解析成 CSS 規則表,而後將每條 CSS 規則按照「從右向左」的方式在 DOM 樹上進行逆向匹配,生成一個具備樣式規則描述的 DOM 渲染樹。接下來就是將渲染樹進行佈局、繪製的過程。首先根據 DOM 渲染樹上的樣式規則,對 DOM 元素進行大小和位置的定位,關鍵屬性如position;width;margin;padding;top;border;...,接下來再根據元素樣式規則中的color;background;shadow;...規則進行繪製。

另外,這個過程是逐步完成的,爲了更好的用戶體驗,渲染引擎將會盡量早的將內容呈現到屏幕上,並不會等到全部的 html 都解析完成以後再去構建和佈局 render 樹。它是解析完一部份內容就顯示一部份內容,同時,可能還在經過網絡下載其他內容。

再者,須要注意的是,在瀏覽器渲染完首屏頁面後,若是對 DOM 進行操做會引發瀏覽器引擎對 DOM 渲染樹的從新佈局和從新繪製,咱們叫作「重排」和「重繪」,因爲重排和重繪是先後依賴的關係,重繪發生時未必會觸發渲染引擎的重排,可是若是發生了重排就必然會觸發重繪操做,這樣帶來的性能損害就是巨大的。所以咱們在作性能優化的時候應該遵循「避免重排;減小重繪」的原則。

不一樣瀏覽器內核間的差別

在不一樣的瀏覽器內核下, 瀏覽器頁面渲染的流程略有不一樣

webkit 內核工做流程

Geoko 內核工做流程

上面兩幅圖分別是 Webkit 和 Geoko 內核渲染 DOM 的工做流程,對比能夠看出,二者的區別主要在於 CSS 樣式表的解析時機,Webkit 內核下,HTML 和 CSS 文件的解析是同步的,而 Geoko 內核下,CSS 文件須要等到 HTML 文件解析成內容 Sink 後才進行解析。

另外描述術語也有不一樣,除此以外二者的流程就基本相同了,其中最重要的三個部分就是 「HTML 的解析」「CSS 的解析」「渲染樹的生成」。這三個部分的原理比較深,會涉及到「詞法分析」「語法分析」「轉換」「解釋」等數據結構的知識,比較枯燥,通常咱們瞭解到這裏就夠了,想深刻了解的同窗能夠閱讀此篇譯文,瀏覽器的工做原理,裏面詳細的解釋了以上三個部分的流程和原理。此處就再也不多作贅述了。

關於 CSS 規則的匹配

上面咱們提到過, CSS 規則是按照「從右向左」的方式在 DOM 樹上進行逆向匹配的,最終生成一個具備樣式規則描述的 DOM 渲染樹。

可是你知道爲何要「從右向左」作逆向匹配嗎?

咱們從新回看【webkit 內核工做流程圖】

webkit 內核工做流程

CSS 規則匹配是發生在webkit引擎的「Attachment」過程當中,瀏覽器要爲每一個 DOM Tree 中的元素擴充 CSS 樣式規則(匹配 Style Rules)。對於每一個 DOM 元素,必須在全部 Style Rules 中找到符合的 selector 並將對應的規則進行合併。選擇器的「解析」實際是在這裏執行的,在遍歷 DOM Tree 時,從 Style Rules 中去尋找對應的 selector。

咱們來舉一個最簡單的栗子:

<template>
<div>
  <div class="t">
    <span>test</span>
    <p>test</p>
  <div>
</div>
</template>

<style>
div{ color: #000; }
div .t span{ color: red; }
div .t p{color: blue; }
</style>

此處咱們有一個 html 元素 和一個 style 元素,二者須要作遍歷匹配

此處會有 4*3 個匹配項,若是作正向匹配,在遇到 <span> 標籤匹配 div .t p{ color: red; } 到匹配項時,計算機首先要找到<span> 標籤的父標籤和祖父標籤,判斷他們是否知足div .t的規則,而後再匹配<span>是否爲p標籤,此處匹配不成功,產生了三次浪費。

若是時逆向匹配,那麼第一次對比<span>是否爲p標籤即可排除此規則,效率更高。

若是將 HTML 結構變複雜,CSS 規則表變龐大,那麼,「逆向匹配」的優點就遠大於「正向匹配」了,由於匹配的狀況遠遠低於不匹配的狀況。另外,若是在選擇器結尾加上通配符「*」,那麼「逆向匹配」的優點就大打折扣了,這也就是不少優化原則提到的「儘可能避免在選擇器末尾添加通配符」的緣由。

極限了想,若是咱們的樣式表不存在嵌套關係,以下:

<template>
  <div class="t">
    <span class="div_t_span">test</span>
    <p class="div_t_p">test</p>
  <div>
</template>

<style>
div{ color: #000; }
.div_t_span{ color: red; }
.div_t_p{color: blue; }
</style

那麼引擎的「Attachment」過程將獲得極大的精簡,效率也是可想而知的,這就是爲何「微信小程序」樣式表不建議使用關係行寫法的緣由。

相關的性能優化

咱們大體能夠在以上案例中看到同瀏覽器渲染引擎相關的可行優化點。

大體爲如下幾種

減小 JS 加載對 Dom 渲染的影響

將 JS 文件放在 HTML 文檔後加載,或者使用異步的方式加載 JS 代碼

避免重排,減小重繪

在作 css 動畫的時候減小使用 width、 margin、 padding 等影響 CSS 佈局對規則,可使用 CSS3 的 transform 代替。另外值得注意的是,在加載大量的圖片元素時,儘可能預先限定圖片的尺寸大小,不然在圖片加載過程當中會更新圖片的排版信息,產生大量的重排。

減小使用關係型樣式表的寫法

直接使用惟一的類名便可最大限度的提高渲染效率,另外儘可能避免在選擇器末尾添加通配符

減小 DOM 的層級

減小無心義的 dom 層級能夠減小 渲染引擎 Attachment 過程當中的匹配計算量

相關文章
相關標籤/搜索