前端學習筆記之 瀏覽器如何渲染頁面?
從 HTML 到 DOM
1. 字節流解碼
把字節數據解碼成字符數據的過程。
2. 輸入流預處理
把字符數據進行統一格式化的過程。
3. 令牌化
令牌化包含兩步:第一步是將字符數據轉化成令牌(Token),第二步是解析 HTML 生成 DOM 樹。
html 代碼的標記過程:
- 初始化爲“數據狀態”(Data State);
- 匹配到字符 <,狀態切換到 “標籤打開狀態”(Tag Open State);
- 匹配到字符 !,狀態切換至 “標籤聲明打開狀態”(Markup Declaration Open State),後續 7個字符可以組成字符串 DOCTYPE,跳轉到 “DOCTYPE 狀態”(DOCTYPE State);
- 匹配到字符爲空格,當前狀態切換至“DOCTYPE 名稱之前狀態”(Before DOCTYPE Name State);
- 匹配到字符串 html,創建一個新的 DOCTYPE 標記,標記的名字爲 “html” ,然後當前狀態切換至 “DOCTYPE 名字狀態”(DOCTYPE Name 之State);
- 匹配到字符 >,跳轉到 “數據狀態” 並且釋放當前的 DOCTYPE 標記;
- 匹配到字符 <,切換到 “標籤打開狀態”;
- 匹配到字符 h,創建一個新的起始標籤標記,設置標記的標籤名爲空,當前狀態切換至 “標籤名稱狀態”(Tag Name State);
- 從字符 h 開始解析,將解析的字符一個一個添加到創建的起始標籤標記的標籤名中,直到匹配到字符 >,此時當前狀態切換至 “數據狀態”並釋放當前標記,當前標記的標籤名爲 “html” 。
- 解析後續的方式與前面一致,創建並釋放對應的起始標籤標記,解析完畢後,當前狀態處於 “數據狀態” ;
- 匹配到字符串 “標記” ,針對每一個字符,創建並釋放一個對應的字符標記,解析完畢後,當前狀態仍然處於 “數據狀態” ;
- 匹配到字符 <,進入 “標籤打開狀態” ;
- 匹配到字符 /,進入 “結束標籤打開狀態”(End Tag Open State);
- 匹配到字符 b,創建一個新的結束標籤標記,設置標記的標籤名爲空,當前狀態切換至“標籤名稱狀態”(Tag Name State);
- 重新從字符 b 開始解析,將解析的字符一個一個添加到創建的結束標籤標記的標籤名中,直到匹配到字符 >,此時當前狀態切換至 “數據狀態”並釋放當前標記,當前標記的標籤名爲 “body”;
- 解析 的方式與 一樣;
- 所有的 html 標籤和文本解析完成後,狀態切換至“數據狀態” ,一旦匹配到文件結束標誌符(EOF),則釋放 EOF 標記。
遇到 script 標籤時的處理:
- 內聯代碼:解析過程暫停,執行權轉給JavaScript 腳本引擎,等待JavaScript執行完成。如果遇到調用document.write() 函數,此時渲染引擎會回到第二步,將這些代碼加入字符流,重新進行解析。
- 外鏈腳本:根據標籤屬性來執行對應的操作。
4. 構建 DOM 樹
瀏覽器在創建解析器的同時會創建一個 Document 對象。
構建 DOM 樹的步驟:
- 進入初始狀態 “initial” 模式;
- 樹構建器接收到 DOCTYPE 令牌後,樹構建器會創建一個 DocumentType節點附加到 Document 節點上,DocumentType 節點的 name 屬性爲 DOCTYPE 令牌的名稱,切換到“before html” 模式;
- 接收到令牌 html 後,樹構建器創建一個 html 元素並將該元素作爲 Document的子節點插入到 DOM 樹中和開放元素棧中,切換爲 “before head” 模式;
- 雖然沒有接收到 head令牌,但仍然會隱式地創建 head 元素並加到 DOM 樹和開放元素棧中,切換到“in head”模式;
- 將開放元素棧中的 head元素彈出,進入 “after head”模式;
- 接收到 body 令牌後,會創建一個 body 元素插入到 DOM樹中同時壓入開放元素棧中,當前狀態切換爲 “in body” 模式;
- 接收到字符令牌,創建 Text 節點,節點值爲字符內容“標記”,將Text 節點作爲 body 元素節點插入到 DOM 樹中;
- 接收到結束令牌 body,將開放元素棧中的 body 元素彈出,切換“after body” 模式;
- 接收到結束令牌 html,將開放元素棧中的 html 元素彈出,切換至 “after after body” 模式;
- 接收到 EOF 令牌,樹構建器停止構建,html 文檔解析過程完成。
從 DOM 到渲染
有了 DOM 樹和 CSSOM 樹之後,渲染引擎就可以開始生成頁面了。
5. 構建渲染樹
將DOM 樹包含的結構內容與 CSSOM 樹包含的樣式規則合併成一棵渲染樹。這個過程會從 DOM 樹的根節點開始遍歷,然後在 CSSOM 樹上找到每個節點對應的樣式。
6. 佈局
佈局就是計算元素的大小及位置。
佈局完成後會輸出對應的“盒模型”,它會精確地捕獲每個元素的確切位置和大小,將所有相對值都轉換爲屏幕上的絕對像素。
7. 繪製
繪製就是將渲染樹中的每個節點轉換成屏幕上的實際像素的過程。
總結
瀏覽器渲染引擎生成頁面的 7 個步驟,前面 4 個步驟爲 DOM 樹的生成過程,後面 3 個步驟是利用 DOM 樹和 CSSOM 樹來渲染頁面的過程。數據的變化過程歸結如下:
字節 → 字符 → 令牌 → 樹 → 頁面