身為與瀏覽器朝夕相處的前端工程師,除了會「使用」它之外,理解背後的架構與運作模式,能幫助我們在更底層的角度找到效能優化的可能性。本章先從瀏覽器架構的演進切入,再走一遍現代瀏覽器的渲染管線(Rendering Pipeline)。
舊石器時代:單一程序架構#
2007 年以前的瀏覽器幾乎都是 Single Process 的設計,所有功能模組都跑在同一個程序裡:JavaScript 執行環境、網路、外掛(Plugin)、渲染引擎等等。這種架構衍生出三個明顯問題:
- 不穩定:任何一個模組崩潰都會把整個瀏覽器拖下水。
- 不流暢:頁面、JS 與外掛共用一條 thread,遇到無限迴圈就會整個失去回應;記憶體也很難完全回收。
- 不安全:缺乏沙盒隔離,外掛或 Script 有機會直接存取系統資源。
function stupidFunc() {
while (true) {
console.log("HI");
}
}
stupidFunc();在 Single Process 的模型下,這段程式會直接吃光整個 thread,讓所有分頁都動彈不得。
新石器時代:多程序架構#
2008 年 Chrome 提出 Multi Processes 架構,把渲染(Renderer Process)與外掛(Plugin Process)獨立成不同程序,再透過 IPC 通訊。它一次解決三個老問題:
- 穩定性:某個分頁或外掛掛掉,只會影響自己所在的程序。
- 流暢性:每個分頁獨立的 Renderer Process 關閉時,作業系統會回收整塊記憶體,自然解決長時間使用造成的 memory bloat。
- 安全性:Plugin Process 與 Renderer Process 都跑在沙盒(Sandbox)中,惡意程式碼無法輕易突破隔離環境。
現代:再進一步拆分#
現在的 Chrome 在多程序基礎上又獨立出 Network Process 與 GPU Process,分別負責網路資源載入與繪製運算。獨立程序帶來穩定、安全與平行化的好處,但同時也付出代價:
- 整體記憶體佔用更高。
- 程序之間需要更多 IPC 通訊,架構複雜度上升。
未來:服務導向架構#
2016 年 Chrome 團隊提出以服務導向架構(SOA, Services Oriented Architecture)為基礎的新模型,將瀏覽器內的功能拆成可組合的「服務」,並依硬體狀況彈性整併:
- 在效能充裕的裝置上,每個服務獨立成一個程序,得到最佳的隔離與平行化。
- 在資源受限的裝置上,會把多個服務合併到單一程序,省下記憶體開銷。
補充說明 SOA 的概念與微服務(Microservices)相近,但兩者在邊界、部署與治理層面仍有差異,這也是為什麼 Chrome 採用 SOA 的角度去重組瀏覽器內部模組。
渲染引擎的運作機制#
每一個分頁都有獨立的 Renderer Process。Chrome 自 2018 年起更進一步推動 Site Isolation:跨來源的 iframe 也會運行在獨立的 Renderer Process,徹底落實同源政策的安全模型。不過符合 Same Site 條件、且由原頁面開啟的新分頁,預設會與原頁面共用同一個 Renderer Process,以節省資源並支援共享的 JavaScript 環境。
當瀏覽器收到 HTML 後,渲染引擎會走過大致以下流程:
- 解析 HTML 產生 DOM Tree。
- 解析 CSS 產生 CSSOM Tree。
- DOM 與 CSSOM 結合形成 Render Tree。
- 進行版面配置(Layout)計算每個元素的位置與大小。
- 對每個節點產生繪製(Paint)指令。
- Compositor Thread 將節點切分成合成層(Compositing Layer)並組合成畫面。
分層與柵格化#
為了支援滾動、3D 等複雜效果,瀏覽器會在 Layout 之後再產生 Layer Tree。Renderer Process 的 Main Thread 在此階段做的並不是真的繪圖,而是「產生繪製指令」。指令完成後,會交給 Compositor Thread,再切成更小的 tile,由 raster threads 負責柵格化(rasterize),並把結果存到 GPU 記憶體中。最後 Compositor Thread 整合 draw quads 產生 Compositor Frame,透過 IPC 送到 Browser Process,再丟給 GPU 顯示。
Reflow、Repaint 與 Compositing#
頁面更新時,會觸發以下三種行為的其中之一:
- Reflow(回流):改變幾何屬性,如 width、height、top,整個渲染流程都得重跑。
- Repaint(重繪):只改變顏色、背景等樣式,跳過版面配置,從繪製重新開始。
- Composition(合成):只動到合成層的屬性(例如 transform),完全跳過 Reflow 與 Repaint,且合成的工作是在 Compositor Thread 與 Raster Thread 上進行,不佔用 Main Thread。
實務建議 CSS 動畫之所以推薦使用
transform,正是因為它能落在 Composition Only 的路徑上,避免昂貴的版面配置與重繪計算。
<style>
.box {
transform: translate(10px, 20px);
will-change: transform;
}
</style>避免多餘的 Reflow 與 Repaint#
- 不要連續用多個 statement 修改 style,建議改用切換 class 的方式一次帶過。
- 把所有讀取(例如
offsetHeight)集中處理完,再一起寫入,可避免反覆觸發 Reflow。 - 對於需要動畫的元素,優先選用
transform與opacity。
理解瀏覽器架構與渲染管線並不會立刻讓網站變快,但它提供了「為什麼這樣寫比較快」的判斷依據。當效能問題出現時,這份底層知識能讓我們直接定位到瓶頸所在。