魯斯前端布魯斯前端

文章中英模式

常見的前端面試題目 - 頁面載入 - 解釋網頁的渲染過程(CRP路徑)

深入解析瀏覽器的關鍵渲染路徑(CRP),包含 DOM、CSSOM、渲染樹的構建過程,以及重排、重繪的優化策略和面試常見問題的完整解答。

影片縮圖

懶得看文章?那就來看影片吧

基本概念

瀏覽器將 HTML、CSS 和 JavaScript 轉換為像素的過程稱為關鍵渲染路徑(Critical Rendering Path,簡稱 CRP)。理解這個過程對於優化網頁效能至關重要。

使用者請求 URL ex: https://www.google.com
伺服器處理請求 ─────────────────────────────┐
     │                                      │
     ├─→ SSR (Server-Side Rendering)        │
     │    伺服器生成完整 HTML                 │
     │                                      │
     ├─→ SSG (Static Site Generation)       │
     │    返回預先生成的靜態 HTML              │
     │                                      │
     └─→ SPA (Single Page Application)      │
          返回基礎 HTML 框架                  │
     │                                      │
     ↓                                      │
HTML 返回瀏覽器 ◄───────────────────────────┘
HTML ──────┐
        DOM Tree      CSS ──────┐
           │                    ↓
           │               CSSOM Tree
           │                    │
           └─────────┬─────────┘
               Render Tree  ◄─── JavaScript 執行
                     │           (可修改 DOM 和 CSSOM)
                  Layout
                   Paint
                Composite

1. HTML 解析 → DOM Tree

瀏覽器將 HTML 轉換為 DOM (Document Object Model),作為網頁的物件表示和 JavaScript 操作介面。解析過程是漸進式的,允許逐步處理內容。

2. CSS 解析 → CSSOM Tree

CSS 被解析為 CSSOM (CSS Object Model),這是渲染阻塞資源,必須完成構建才能進行下一步。

3. JavaScript 執行

JavaScript 是解析阻塞資源,當瀏覽器遇到 script 標籤時,會暫停 DOM 構建,先下載並執行 JavaScript。JavaScript 可以修改 DOM 和 CSSOM,因此必須等待前面的 CSS 解析完成。使用 async 或 defer 屬性可改變執行時機。

4. DOM + CSSOM → Render Tree

結合 DOM 和 CSSOM 形成渲染樹,只包含可見元素(display: none 的元素會被排除)及其樣式信息。

5. Layout(Reflow)

計算每個可見元素的精確位置和大小,確定其在視窗中的定位。視窗大小改變時會觸發重新布局。

6. Paint(Repaint)

將布局轉換為實際像素,包括文字、顏色、邊框、陰影等視覺效果,通常在多個層上進行。

7. Composite

將不同繪製層合成最終畫面,處理層疊順序和透明度。使用 GPU 加速可提升性能。

效能優化建議

最小化關鍵資源

  • 減少 HTML、CSS、JavaScript 檔案大小
  • 移除未使用的 CSS/JavaScript
  • 使用 Code Splitting 和延遲加載

減少重排和重繪

  • 批量修改 DOM
  • 使用 CSS Transform 和 Opacity 進行動畫
  • 避免頻繁改變元素位置和大小

🔥 常見面試題目

(一)什麼是 Reflow 和 Repaint?它們的區別是什麼?

解答:

  • Reflow(重排):當元素的大小、位置或文檔流中的位置發生改變時觸發
  • Repaint(重繪):當元素外觀改變但不影響佈局時觸發,如顏色、背景等變化
  • Reflow 一定會導致 Repaint,但 Repaint 不一定會導致 Reflow

(二)為什麼說 CSS 是渲染阻塞資源?

解答:

  • CSS 被視為渲染阻塞資源是因為瀏覽器需要等待 CSSOM 完全構建後才能進行渲染樹的構建
  • 即使 JavaScript 不依賴 CSS,如果存在未完成下載的樣式表,JavaScript 執行也會被阻塞
  • 這是為了確保頁面渲染的正確性和一致性

(三)會影響 Reflow 和 Repaint 的 CSS 屬性有哪些?如何避免頻繁觸發?

解答:

  • 觸發 Reflow 的屬性:width、height、margin、padding、border、position、top、left、right、bottom、font-size、font-family、display、float
  • 主要觸發 Repaint 的屬性:color、background、visibility、text-decoration、box-shadow、outline
  • 高效能屬性:transform、opacity、filter(這些屬性通常只觸發合成層,由 GPU 加速處理,不觸發 CPU 密集的 Reflow 操作)

避免頻繁觸發的策略:

// ------------------------- 1. 批量修改 DOM 而非逐個修改 -------------------------
// 不好的做法 - 多次觸發 Reflow
const element = document.getElementById('myElement');
element.style.width = '100px';
element.style.height = '200px';
element.style.margin = '10px';

// 好的做法 - 只觸發一次 Reflow
const element = document.getElementById('myElement');
element.classList.add('new-layout');
// 或使用 style.cssText
element.style.cssText = 'width: 100px; height: 200px; margin: 10px;';

/* CSS */
.new-layout {
  width: 100px;
  height: 200px;
  margin: 10px;
}

// ------------------------- 2. 使用 DocumentFragment 批量添加多個元素 -------------------------
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li);
}
document.getElementById('myList').appendChild(fragment);

// ------------------------- 3. 使用 transform 代替位置調整 -------------------------
// 不好的做法 - 觸發 Reflow
element.style.left = '10px';
element.style.top = '20px';

// 好的做法 - 只觸發合成
element.style.transform = 'translate(10px, 20px)';

// ------------------------- 4. 先設置 display: none,修改後再顯示 -------------------------
element.style.display = 'none';
// 進行多次修改...
element.style.width = '100px';
element.style.height = '200px';
element.style.margin = '10px';
// 最後再顯示,只觸發一次 Reflow
element.style.display = 'block';