TL;DR | 面試情境模擬 #
👴 面試官:瀏覽器渲染的流程是什麼?Reflow 和 Repaint 有什麼差別?
🧑💻 你:瀏覽器拿到 HTML 後依序做:解析 HTML 建立 DOM Tree、解析 CSS 建立 CSSOM Tree、合併成 Render Tree、計算每個元素的位置和大小(Layout)、決定每個像素的顏色(Paint)、把各層合併到螢幕(Composite)。Reflow 是改變了影響排版的屬性(如 width、height、font-size),要重新從 Layout 開始算,成本最高。Repaint 只改了顏色或背景,不影響排版,跳過 Layout 直接 Paint。Composite 只改了 transform 或 opacity,只在合成層做,不影響 Layout 和 Paint,成本最低。
比喻:蓋房子 #
DOM = 房子的磚牆結構
CSSOM = 設計圖(顏色、材質、大小)
Render Tree = 知道哪些磚塊要顯示,合計後的施工計劃
Layout = 測量每塊磚的實際位置和尺寸
Paint = 粉刷每塊磚
Composite = 把各個房間拼在一起變成完整的家
完整渲染流程 #
HTML bytes
│
▼
DOM Tree + CSSOM Tree
│ │
└──── 合併 ────┘
│
Render Tree(只含可見元素)
│
Layout(計算位置和尺寸)
│
Paint(像素著色)
│
Composite(圖層合成)
│
螢幕
各階段說明 #
| 階段 | 做什麼 | 觸發範例 |
|---|---|---|
| DOM Construction | 解析 HTML tag,建立節點樹 | 初次載入 |
| CSSOM Construction | 解析 CSS,建立樣式規則樹 | 初次載入 |
| Render Tree | 合併 DOM + CSSOM,排除 display:none 的節點 |
初次載入 |
| Layout(Reflow) | 計算每個元素的位置、大小 | width/height/margin 改變 |
| Paint(Repaint) | 填充像素顏色、背景、邊框 | color/background 改變 |
| Composite | 把各圖層合成最終畫面 | transform/opacity 改變 |
Reflow vs Repaint vs Composite #
Reflow(最貴):
改變任何影響排版的屬性
width, height, padding, margin, font-size, top, left...
→ 從 Layout 重新算 → Paint → Composite
Repaint(中等):
改變不影響排版的視覺屬性
color, background, visibility, border-color...
→ 跳過 Layout → 從 Paint 開始
Composite(最便宜):
只改合成層屬性
transform, opacity
→ 跳過 Layout 和 Paint → 只做 Composite
→ 可以在 GPU 上執行,不阻塞主執行緒
效能優化:能用 transform 就不用 top/left #
/* 慢:觸發 Reflow */
.box { left: 100px; top: 50px; }
/* 快:只觸發 Composite */
.box { transform: translate(100px, 50px); }
JavaScript 和渲染的關係 #
JS 執行 → Style → Layout → Paint → Composite
JavaScript 跑在主執行緒,和渲染競爭。如果 JS 執行時間超過 16ms(60fps 的一格),就會掉幀(Jank)。
優化方法:
- 長任務拆成小塊,用
requestAnimationFrame在渲染前執行 - 避免 Layout Thrashing(反覆讀寫 DOM 幾何屬性)
- 耗時計算放到 Web Worker
💡 面試官可能會追問 #
Q1:script 標籤放在 head 還是 body 底部,有什麼差別? #
<script> 預設會阻塞 HTML 解析(因為 JS 可能修改 DOM)。放在 <head> 且沒有 async/defer 屬性,會暫停 DOM 建構去下載並執行,導致頁面白屏時間變長。解法:加 defer(HTML 解析完才執行)或 async(下載完立刻執行,不等 HTML)。
Q2:什麼是 Critical Rendering Path? #
首次渲染必須完成的最小路徑:HTML 解析 → CSS 解析 → Render Tree → Layout → Paint。優化 CRP 的目標是讓這條路越短越快,例如:精簡 Critical CSS、延遲非必要 JS、使用 resource hints(preload/preconnect)。
Q3:will-change 有什麼作用? #
.animated { will-change: transform; }
告訴瀏覽器這個元素即將改變,提前把它提升為獨立的合成層,讓之後的動畫只走 Composite,不觸發 Reflow 和 Repaint。但不要濫用,每個合成層都佔記憶體。