浏览器的渲染过程是将 HTML、CSS 和 JavaScript 转换成用户可见的网页的过程。这个过程涉及多个关键步骤,从解析文档到最终绘制像素到屏幕上。下面详细介绍浏览器的渲染流程,并结合具体示例说明。
1. 浏览器渲染的核心流程
浏览器渲染主要分为以下几个阶段:
- 解析 HTML → 构建 DOM 树
- 解析 CSS → 构建 CSSOM 树
- 合并 DOM 和 CSSOM → 生成 Render Tree(渲染树)
- 计算布局(Layout / Reflow)
- 绘制(Paint)
- 合成(Composite) (现代浏览器优化)
2. 详细步骤解析
(1)解析 HTML → 构建 DOM 树
-
输入:HTML 文档(字符串)
-
输出:DOM(Document Object Model)树,表示网页的结构
-
过程:
- 浏览器逐行读取 HTML,解析标签并生成节点。
- 遇到
<script>
或<link>
等外部资源时会阻塞解析(除非标记async
/defer
)。
示例
xml
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
</head>
<body>
<h1>Hello World</h1>
<p>This is a paragraph.</p>
</body>
</html>
DOM 树结构:
- html
- head
- title
- "Demo"
- body
- h1
- "Hello World"
- p
- "This is a paragraph."
(2)解析 CSS → 构建 CSSOM 树
-
输入 :CSS 样式表(内联、外部、
<style>
) -
输出:CSSOM(CSS Object Model)树,表示样式规则
-
特点:
- CSS 解析是 层叠的 (
!important
、选择器优先级)。 - CSS 解析会 阻塞渲染(Render Blocking)。
- CSS 解析是 层叠的 (
示例
css
h1 { color: red; }
p { font-size: 16px; }
CSSOM 树结构:
css- h1 - color: red - p - font-size: 16px
(3)合并 DOM + CSSOM → 生成 Render Tree
-
输入:DOM + CSSOM
-
输出 :Render Tree(仅包含 可见元素 ,如
display: none
的元素不会进入 Render Tree) -
关键点:
- 只包含 需要渲染的节点 (如
visibility: hidden
仍会占据空间,所以会进入 Render Tree)。 - 伪元素(
::before
,::after
)也会被包含。
- 只包含 需要渲染的节点 (如
示例
假设 DOM 和 CSSOM 如上,Render Tree 结构:
less- body - h1 (color: red) - "Hello World" - p (font-size: 16px) - "This is a paragraph."
(4)计算布局(Layout / Reflow)
-
作用 :计算每个元素在屏幕上的 精确位置和大小
-
触发条件:
- 首次加载
- 窗口大小变化(
resize
) - 修改 DOM 或样式(如
width
、margin
)
示例
css
p { width: 50%; margin: 10px; }
浏览器会计算:
p
的宽度 = 父容器宽度 × 50%- 外边距
margin: 10px
(5)绘制(Paint)
-
作用 :将 Render Tree 转换成屏幕上的 像素
-
过程:
- 遍历 Render Tree,调用底层图形 API(如 Skia in Chrome)绘制颜色、边框、文本等。
- 可能涉及 分层绘制 (如
transform
会单独一层)。
示例
h1
被绘制为红色文本p
被绘制为 16px 的黑色文本
(6)合成(Composite)
-
作用:将不同图层(Layers)合并成最终画面
-
优化手段:
- GPU 加速(
transform
、opacity
等属性会触发 GPU 合成) - 减少重绘(Repaint)和回流(Reflow)
- GPU 加速(
示例
css
.box { transform: translateZ(0); } /* 强制 GPU 加速 */
3. 关键渲染路径优化
(1)减少阻塞
-
CSS:
- 使用
<link rel="stylesheet" media="print">
避免非关键 CSS 阻塞渲染。 - 内联关键 CSS(Critical CSS)。
- 使用
-
JavaScript:
- 使用
async
或defer
延迟脚本加载。
- 使用
(2)减少回流(Reflow)和重绘(Repaint)
-
避免频繁修改样式:
ini// ❌ 糟糕:多次触发回流 el.style.width = '100px'; el.style.height = '200px'; // ✅ 优化:使用 class 或 requestAnimationFrame el.classList.add('new-size');
-
使用
transform
和opacity
(只触发合成,不触发回流)。
(3)使用开发者工具分析
-
Chrome DevTools → Performance 面板:
- 查看 Layout、Paint、Composite 耗时。
- 检测强制同步布局(Forced Synchronous Layout)。
4. 完整示例
场景:动态添加列表项
ini
<ul id="list"></ul>
<button onclick="addItems()">Add Items</button>
<script>
function addItems() {
const list = document.getElementById('list');
// ❌ 糟糕:每次循环都触发回流
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
list.appendChild(li); // 触发 100 次回流
}
// ✅ 优化:使用 DocumentFragment 批量操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
list.appendChild(fragment); // 只触发 1 次回流
}
</script>
5. 总结
阶段 | 关键点 | 优化建议 |
---|---|---|
DOM 构建 | 解析 HTML → 生成 DOM | 减少 DOM 深度,避免复杂嵌套 |
CSSOM 构建 | 解析 CSS → 生成 CSSOM | 内联关键 CSS,异步加载非关键 CSS |
Render Tree | 合并 DOM + CSSOM | 避免 display: none 滥用 |
Layout | 计算元素位置/大小 | 避免强制同步布局 |
Paint | 填充像素 | 减少复杂阴影/渐变 |
Composite | 合并图层 | 使用 transform /opacity 优化 |
通过理解浏览器的渲染机制,可以更高效地优化网页性能,减少卡顿和加载时间。