鲁斯前端布鲁斯前端

文章中英模式

常见的前端面试题目 - 页面加载 - 解释网页的渲染过程(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';