應該有很多前端開發人員都思考過這麼一個問題:從輸入 URL 到頁面載入完成,中間都做發生了什麼?

這個問題涉及的面非常廣,每個涉及的點又很深入。從觸屏/鍵盤如何到 CPU?CPU 如何到系統核心?如何從作業系統 GUI 到瀏覽器?瀏覽器如何向網絡卡傳送資料?資料如何從本機網絡卡傳送到伺服器?伺服器接收資料後如何處理?伺服器返回資料後瀏覽器如何處理?瀏覽器如何將頁面展現出來?等等等等,每一個過程都包含了大量且深入的知識體系,很難一以貫通。

但作為前端開發人員,瀏覽器是我們的主要工具之一,瀏覽器是如何將頁面展現出來的則是我們更關注的部分。因此本文就從一些基本流程來簡要描述這個過程。

從上面這個圖中可以發現,雖然使用的 Javascript 是單執行緒語言,但瀏覽器本身是多程序的。

但是這並不是從一而終的狀態,而是瀏覽器從早期的單程序結構逐漸發展發展而來。現代瀏覽器各程序根據負責的功能不同,分為瀏覽器程序、渲染器程序、網路程序、GPU 程序、快取程序、外掛程序等等。為了更好的理解瀏覽器頁面的呈現過程,我們以最主流的 Chrome 為例,簡要的說明一下各個程序的大致職能:

  • 瀏覽器程序: 負責控制介面展示、使用者互動、子程序管理等功能。

  • 渲染器程序: 負責將 HTML\CSS\JS 轉化為使用者可以與之互動的網頁。渲染引擎如 webkit、blink 和 JS 引擎 V8 都是在該程序之中。

  • GPU 程序: GPU 程序原本是為了實現 3D CSS 效果,但是隨後頁面、Chrome 的 UI 都採用 GPU 來繪製,是 GPU 成為了重要需求,於是增加了 GPU 程序。

  • 網路程序: 負責頁面的網路資源載入。

  • 外掛程序:負責外掛的執行,由於外掛可能崩潰,需要外掛程序其他程序隔離。注意,外掛並不是我們常用的瀏覽器拓展,plugin 和 extension 是不同的。

  • 快取程序:負責處理頁面資源快取和清理。

我們本次需要重點關注的是渲染器程序。

回到問題,當我們在瀏覽器位址列輸入地址時,瀏覽器程序的 UI 執行緒會捕捉輸入內容,如果訪問的是網址,那麼 UI 執行緒會啟動一個網路執行緒來構建請求(這裡我們暫時不考慮快取,快取又是另外一個故事了),它請求 DNS 進行域名解析然後連線伺服器獲取資料。如果我們輸入的是關鍵詞,瀏覽器則使用預設配置的搜尋引擎來搜尋。在獲取到資料並通過安全校驗後,網路執行緒會通知 UI 執行緒資料準備完畢,然後UI執行緒建立一個渲染器程序來進行頁面的渲染,並將資料通過 IPC 管道傳遞給渲染器程序。

至此,我們的主角渲染器程序登場!

解析 HTML

渲染器程序接收到的是一個 HTML,需要把 HTML 解析成 DOM 資料結構。因為直接的 HTML 位元組流是無法被渲染引擎所理解的,必須轉化成可以理解的內部結構。這個內部結構就是 DOM,DOM 提供了對 HTML 文件的結構化表述。在渲染引擎中,DOM 有三個層面的作用:

  • 從頁面角度:DOM 是生成頁面的基礎資料結構。

  • 從 js 角度:DOM 提供了 js 操作的介面。通過這套介面,js 可以對 DOM 介面進行訪問,從而使開發者擁有改變文件結構、樣式、內容的能力。

  • 從安全形度:DOM 是 HTML 經過解析的內部資料結構,它將 web 頁面和 js 連結起來,並過濾了一些不安全的內容。

渲染器程序內部使用 HTML Parser 將 HTML 解析成 DOM 結構。需要注意的是,HTML 解析器不會等待整個 HTML 文件載入完畢再去解析,而是載入多少了多少 HTML,就解析多少。

那麼 HTML 位元組流是如何轉換成 DOM 的呢?

其實和 V8 解析 js 類似,也是做詞法分析,通過分詞器將位元組流成功成一個個 token,包括 Tag token 和文字 token。HTML 解析器維護了一個 token 棧結構,token 會按照對應順序入棧出棧,然後將 token 解析成 DOM 節點,並將 DOM 節點新增進 DOM 樹中。

前面提到生成 DOM 可以過濾一些不安全內容。這主要是渲染引擎中的一個名為XSSAuditor 安全檢查模組實現的。它會監測詞法安全,在分詞器解析出 token 之後,檢查這些模組是否引用了外部指令碼,是否符合 CSP 規範,是否存在跨站點請求等。如果出現不符合規範的內容。XSSAuditor 會對該指令碼或下載任務進行攔截。

DOM 樹在構建過程中會建立 document 物件,然後以 document 為根節點的 DOM 樹不斷修改向其中新增新的元素。

解析 CSS

前面已經將 HTML 解析成 DOM 樹了,但是光擁有 DOM 樹還不足以讓我們知道頁面的樣貌。因為我們肯定會為頁面設定一些樣式。因此主程序還會解析頁面中的 CSS 從而確定每個 DOM 節點的計算樣式(computed style)。

CSS 的樣式來源主要有三個:

  • 通過 link 引用的外部 CSS 檔案

  • 使用