浏览器渲染引擎工作原理
约 1694 字大约 6 分钟
browserrendering
2025-07-19
概述
浏览器渲染引擎(也称排版引擎或布局引擎)负责将 HTML、CSS 和 JavaScript 转换为用户可见的交互式页面。理解渲染流水线的每个阶段,是进行前端性能优化的基础。主流渲染引擎包括 Chromium 的 Blink、Firefox 的 Gecko 和 Safari 的 WebKit。
渲染流水线总览
1. Navigation 阶段
当用户在地址栏输入 URL 或点击链接时,浏览器依次执行:
- DNS 解析 — 将域名解析为 IP 地址
- TCP 握手 — 建立传输连接(HTTPS 还需 TLS 握手)
- HTTP 请求 — 发送请求并接收响应
浏览器收到第一个 HTML 字节后即进入解析阶段,这也是 TTFB(Time To First Byte) 指标的衡量点。
2. HTML 解析与 DOM 构建
HTML 解析器将字节流经过以下步骤转换为 DOM 树:
Bytes → Characters → Tokens → Nodes → DOM Tree<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<title>Demo</title>
</head>
<body>
<div class="container">
<h1>Hello</h1>
<p>World</p>
</div>
<script src="app.js"></script>
</body>
</html>解析过程中的阻塞行为
- CSS 是渲染阻塞资源:浏览器必须构建完 CSSOM 才能生成渲染树
- JavaScript 是解析阻塞资源:遇到
<script>标签时解析暂停,除非使用async或defer
<!-- 阻塞解析 -->
<script src="app.js"></script>
<!-- 异步加载,下载完立即执行 -->
<script async src="analytics.js"></script>
<!-- 延迟执行,DOMContentLoaded 前按序执行 -->
<script defer src="app.js"></script>Preload Scanner
现代浏览器在主解析器被脚本阻塞时,会启动预加载扫描器(Preload Scanner)提前发现并请求后续资源,显著减少资源加载的等待时间。
3. CSS 解析与 CSSOM 构建
浏览器将 CSS 文本解析为 CSSOM(CSS Object Model) 树。CSSOM 包含了所有样式规则及其优先级信息。
CSSOM 的构建必须等待所有 CSS 下载并解析完毕,因此减少关键 CSS 的体积对首屏性能至关重要。
4. 渲染树(Render Tree)构建
渲染树由 DOM 树和 CSSOM 树合并而成,只包含可见节点:
display: none的元素 不 出现在渲染树中visibility: hidden的元素 会 出现(仍占据布局空间)<head>、<script>等非可视元素被排除
// 伪代码演示渲染树构建逻辑
function buildRenderTree(domNode, cssom) {
if (!domNode.isVisible()) return null;
const style = cssom.computeStyle(domNode);
if (style.display === 'none') return null;
const renderNode = new RenderNode(domNode, style);
for (const child of domNode.children) {
const childRender = buildRenderTree(child, cssom);
if (childRender) renderNode.appendChild(childRender);
}
return renderNode;
}5. Layout(布局/回流)
布局阶段计算每个渲染树节点的精确位置和尺寸。从根节点开始递归处理,输出一个盒模型(Box Model)。
触发回流(Reflow)的操作
// 这些操作都会触发回流
element.offsetHeight; // 读取布局属性
element.getBoundingClientRect();
element.style.width = '200px'; // 修改几何属性
window.getComputedStyle(el); // 获取计算样式
// 批量修改减少回流
// Bad: 多次触发
el.style.width = '100px';
el.style.height = '200px';
el.style.margin = '10px';
// Good: 一次触发
el.style.cssText = 'width:100px;height:200px;margin:10px';
// 或使用 class 切换
el.classList.add('new-layout');6. Paint(绘制)
绘制阶段将渲染树中每个节点转换为屏幕上的实际像素。浏览器会为不同的内容创建绘制记录(Paint Records)。
绘制顺序遵循 CSS 堆叠上下文(Stacking Context):
- 背景色
- 背景图
- 边框
- 子元素
- 轮廓(Outline)
触发重绘(Repaint)
修改不影响布局的属性只触发重绘:
/* 仅触发重绘,不触发回流 */
.element {
color: red;
background-color: blue;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
visibility: hidden; /* 不同于 display:none */
}7. Compositing(合成)
现代浏览器将页面分为多个合成层(Composite Layers),各层独立光栅化后由 GPU 合成为最终画面。
创建独立合成层的方式
/* 以下属性会提升元素到独立合成层 */
.gpu-layer {
will-change: transform; /* 推荐方式 */
transform: translateZ(0); /* hack 方式 */
backface-visibility: hidden; /* hack 方式 */
}合成层的优点:transform 和 opacity 动画可以完全在合成线程执行,不阻塞主线程。
关键渲染路径优化
优化策略
<!-- 1. 关键 CSS 内联 -->
<style>
/* 首屏关键样式内联到 HTML */
.hero { display: flex; min-height: 100vh; }
</style>
<!-- 非关键 CSS 异步加载 -->
<link rel="preload" href="non-critical.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<!-- 2. 资源预加载 -->
<link rel="preconnect" href="https://api.example.com">
<link rel="preload" href="critical-font.woff2" as="font"
type="font/woff2" crossorigin>
<!-- 3. JavaScript 不阻塞渲染 -->
<script defer src="app.js"></script>性能指标对应关系
| 渲染阶段 | 对应性能指标 |
|---|---|
| DOM 构建完成 | DOMContentLoaded |
| 首次内容绘制 | FCP (First Contentful Paint) |
| 最大内容绘制 | LCP (Largest Contentful Paint) |
| 布局稳定 | CLS (Cumulative Layout Shift) |
| 交互就绪 | TTI (Time to Interactive) |
GPU 加速与合成优化
// 使用 requestAnimationFrame 确保动画帧与浏览器刷新同步
function animate() {
// transform 动画只触发合成,性能最佳
element.style.transform = `translateX(${position}px)`;
requestAnimationFrame(animate);
}
// 避免在 rAF 中读取布局属性(会导致强制同步布局)
// Bad
function badAnimate() {
element.style.width = element.offsetWidth + 1 + 'px'; // 强制同步布局
requestAnimationFrame(badAnimate);
}Reflow 与 Repaint 对比
性能消耗排序:Reflow > Repaint > Composite Only。编写高性能动画应尽量只触发合成操作,使用 transform 和 opacity 替代 top/left/width/height 等属性。
总结
浏览器渲染流水线是一个精密的多阶段过程。掌握从 Navigation 到 Compositing 的完整链路,理解回流、重绘和合成的触发条件与性能差异,是进行前端性能优化的核心知识。在实际开发中,应当减少关键渲染路径上的资源数量和体积、避免不必要的回流、善用 GPU 合成层来实现流畅的动画效果。
贡献者
更新日志
9f6c2-feat: organize wiki content and refresh site setup于