浏览器解析与渲染核心步骤
- 解析 HTML,构建 DOM 树
- 解析 CSS,构建 CSSOM 树
- 合并 DOM 树和 CSSOM 树,生成渲染树(Render Tree)
- 布局阶段(Layout/Reflow),计算每个节点的大小与位置
- 绘制阶段(Painting),将节点绘制为像素
- 合成阶段(Compositing)
详细解析
1. 解析 HTML,构建 DOM 树
流程:
- 字节流解码:将网络接收的二进制字节流编码为字符(根据文档声明的编码格式,如 UTF-8)
- 词法分析:将字符流拆分为标记(Token),如
<div>
、id="app"
等 - 语法分析:根据 HTML 规范,将 Token 转换为DOM 节点,最终形成树形结构 DOM 树
简化记忆:网络字节流->字符流->token->DOM 树
关键特性:
- 流式解析:边下载边解析,无须等待完整的 HTML 文档。
- 预加载扫描器:
- 提前扫描文档,并行下载 CSS、JS、图片等资源(prefetch、preload)
- 进行 DNS 预解析(dns-preconnect、dns-prefetch)
- 阻塞行为
- 同步脚本:遇到同步脚本
<script>
(未设置async/defer
)时,暂停解析 HTML,先下载并执行脚本,再继续解析 HTML。 - 外部 CSS:CSS 加载不阻塞 DOM 树构建,但会阻塞渲染
- 同步脚本:遇到同步脚本
2. 解析 CSS,构建 CSSOM 树
处理 3 个来源的样式规则:<style>标签
、*.css外部样式表
、元素内联样式(style属性)
流程:
- 解析 CSS 文件:将 CSS 规则转换为样式规则(Style Rules)
- 构建 CSSOM 树:根据层叠规则(如选择器优先级、继承关系),生成
CSS对象模型(CSSOM)
简化记忆:CSS文本->样式规则->CSSOM树
关键特性:
- 从右向左匹配选择器
- 级联与继承:处理
!important
、内联样式、媒体查询等逻辑 - 阻塞渲染:CSSOM 必须与 DOM 合并后才能生成渲染树,所有CSS 解析会阻塞渲染
- 优化策略:
- 内联关键 CSS,减少请求
- 使用
media
属性异步加载非关键 css(如<link media="print">
)
3. 构建渲染树(Render Tree)
渲染树 = 可见的 DOM 节点 + 对应的 CSSOM 样式
流程
- 合并 DOM 与 CSSOM:筛选出可见节点(如排除
display: none
的元素),计算每个节点的最终样式 - 生成渲染树:包含所有可见节点的样式信息,但不包括大小和位置信息(需布局阶段计算)
简化记忆:DOM树 + CSSOM树 -> 过滤不可见节点 -> 渲染树
过滤规则
- 完全隐藏的节点不会出现在渲染树中(如
display: none
) visibility: hidden
的元素会包含在渲染树中,但不可见- 尺寸为 0(
width:0;heigth:0
)的元素会包含在渲染树中,但不可见
关键特性
- 节点匹配:将 DOM 节点与 CSS 选择器匹配,应用最终样式
- 优化手段:
- 避免复杂 CSS 选择器(如嵌套过深),减少匹配时间
- 使用
content-visibility: auto
跳过不可见区域渲染
4. 布局阶段(Layout/Reflow)
计算每个节点的大小与位置,确定元素在页面上的精确位置。
流程
- 计算几何信息:根据视口(Viewport)大小、盒模型规则,确定每个节点的位置和尺寸
- 处理布局约束:如浮动(Float)、定位(Position)、弹性布局(Flex)、网格布局(Grid)等
简化记忆:渲染树 -> 计算几何信息 -> 确定布局位置
关键特性
- 全局布局和增量布局:
- 首次加载触发全局布局(计算所有节点)
- 后续修改(如调整窗口大小)触发增量布局(仅计算受影响的节点)
- 性能瓶颈:频繁布局(如动画)会导致重排(Reflow),显著影响性能
- 优化建议:
- 使用
transform
、opacity
触发 GPU 加速(跳过布局和绘制) - 批量操作 DOM 时使用
DocumentFragment
(减少重排次数)
- 使用
5. 绘制阶段(Painting)
流程
- 生成绘制列表:将渲染树转换为绘制指令(如:颜色填充、绘制边框等)
- 分层处理:
- 普通层:在默认复合层绘制
- 独立层:会触发硬件加速(transfrom/opacity 等)
- 栅格化:将绘制指令转换为屏幕上的像素(通常在合成线程完成)
- 分块处理:将图层划分为多个图块
简化记忆:布局树 -> 绘制指令 -> 分层处理 -> 栅格化
关键特性
- 分层与合成:浏览器将页面分为多个图层(Layer),独立绘制后合成(如使用
will-change
创建新图层) - 重绘(Repaint):样式变化不影响布局时(如修改颜色),仅触发重绘,跳过布局。
6. 合成阶段
流程:
- 图层合并:将各图层按照
z-index
顺序合并到最终画面 - GPU 加速:利用 GPU 处理
transfrom
、opacity
等属性,提升动画性能
简化记忆:图层 -> GPU处理 -> 最终画面
关键特性:
- 合成线程独立于主线程:避免 Javascript 或样式计算阻塞渲染
- 优化手段:
- 使用
requestAnimationFrame
对齐浏览器刷新频率(通常 60Hz) - 避免强制同步布局(如读取
offsetHeight
后立即修改样式)
- 使用
性能优化策略
- 阻塞渲染的因素
资源类型 | 阻塞行为 |
---|---|
同步 JavaScript | 阻塞 DOM 构建和渲染,直到脚本下载并执行完毕。 |
外部 CSS | 阻塞渲染树生成,但不阻塞 DOM 构建。 |
图片、字体等 | 不阻塞渲染,但可能占用带宽和线程资源。 |
- 优化关键渲染路径(Critical Rendering Path, CRP)
- 目标:最小化首次渲染时间(FCP)
- 策略:
- 压缩与合并资源:减少 HTMl、CSS、JS、图片资源等文件体积
- 异步加载非关键资源:使用
async/defer
延迟执行脚本,preload
提前加载关键资源 - 内联关键 CSS/JS:避免因网络延迟阻塞渲染
- 服务端渲染:直接输出初始 HTML,减少客户端解析时间
扩展问题
- 是什么?有什么作用?
它是HTML 文档类型声明,位于 HTML 文档的首行,用于明确告诉浏览器当前文档遵循的 HTML 标准版本
核心作用
- 触发浏览器的标准模式:
- 无时,浏览器会进入怪异模式,以兼容旧版本 HTML 的渲染方式(如 IE5 布局),导致页面样式和布局与现代标准不一致。
- 有时,浏览器会进入标准模式,严格遵循 W3C 规范解析页面,确保 CSS 和 JS 都正确执行。
- 识别 HTML 版本
- 是HTML5的简化声明
- 旧版本声明对比
<!-- HTML4.01 Strict --> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <!-- XHTML 1.0 --> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- 避免浏览器兼容问题
- 统一不同浏览器(Chrome/Firefox/Edge/Safari)的渲染逻辑,减少布局错位、盒模型计算错误等问题
- 什么是重绘与回流?其触发方式有哪些?
概念:
- 重排/回流:元素的几何属性或布局结构发生改变(如尺寸、位置、内容变化),导致浏览器需要重新计算元素的几何信息并更新渲染树和布局的过程。
- 重绘:元素的外观属性发生改变(如颜色、背景、边框度),导致浏览器需要重新绘制元素外观的过程,无需重新计算布局。
常见的触发方式:
- 重排/回流
- 几何属性修改:如
width
、height
、padding
、margin
、left
、top
等 - 布局结构变化:如
增删DOM节点
、文本内容修改(innerText)
、display: none
等 - 浏览器环境变化:如
窗口缩放resize()
、字体大小调整
- 读取布局属性:如
offsetTop
、offsetWidth
、getComputedStyle
等 - css 布局模式改变:如
切换flex/grid布局属性
、修改positon为absolute/fixed
- 几何属性修改:如
- 重绘
- 颜色与背景修改:如
color
、background
、border-color
、box-shadow
等 - 可见属性调整:如
opacity
、visibility
等(部分浏览器可能会触发合成,而非重绘) - 字体样式调整:如修改
font-family
、text-decoration
等(不影响布局的文本属性) - 非几何动画:使用
transform
或opacity
实现的动画(通常触发合成层优化)
- 颜色与背景修改:如
- script 放在 head 与 body 的区别是什么?
解析与执行时机
<head>
中- 浏览器在解析 HTML 时遇到
<script>
会立即下载并执行脚本,随后继续解析后续内容 - 若脚本未使用
async
或defer
,则脚本会阻塞 HTML 解析和页面渲染,导致用户看到空白页面时间延长 - 若脚本尝试操作 DOM 元素(如
document.getElementById()
),可能因为元素未加载而报错或返回null
(需要通过window.onload
或DOMContentLoaded
事件延迟执行)
- 浏览器在解析 HTML 时遇到
<body>
尾- 脚本会在DOM 树构建完成后执行,确保页面主要内容已渲染完成
- 用户能更快看到页面内容,减少感知延迟
- 所有 DOM 元素已加载完成,可直接操作元素,无须等待事件
- script 标签的 defer 和 async 属性的区别?
共同点:
- 异步加载:两者都表示脚本的下载过程不阻塞 HTML 解析
- 仅适用于外部脚本:必须包含
src
属性(内联脚本无效)
核心区别
属性 | 执行时机 | 执行顺序 | 适用场景 |
---|---|---|---|
async | 下载完成后立即执行,此时 HTML 解析可能暂停 | 不保证顺序(先下载完的先执行) | 独立且不依赖其他脚本的代码(如统计、广告) |
defer | 延迟到 HTML 解析完成后、DOMContentLoaded 事件前执行 | 严格按 HTML 中的顺序执行 | 需要访问完整 DOM 或依赖其他脚本的代码(如页面初始化) |
执行流程对比
- 普通
<script>
(无属性)
HTML解析 → 遇到 <script> → 暂停HTML解析 → 下载脚本 → 执行脚本 → 继续解析 HTML
async
HTML解析 → 遇到 <script> → 继续HTML解析(并行下载脚本) → 脚本下载完成 → 暂停HTML解析 → 执行脚本 → 继续解析 HTML
defer
HTML解析 → 遇到 <script> → 继续HTML解析(并行下载脚本) → 继续HTML解析 → HTML解析完成 → 按顺序执行所有defer脚本 → 触发DOMContentLoaded事件
- preload 与 prefetch 的区别?
preload(预加载当前关键资源)
- 语法
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin />
- 特点:
- 立即加载:浏览器以高优先级下载资源,不等待解析到实际使用位置
- 必须指定
as
:明确资源类型(如font
、image
、script
等),否则可能重复下载 - 应用场景:
- 首屏字体文件(避免页面渲染后字体闪烁)
- 关键 css/js 加载(拆分代码后提前加载)
- 首屏大图
- 注意事项
- 滥用会导致性能下降:仅预加载真正关键的资源。
- 需实际使用:预加载的资源必须在页面中真正使用,否则控制台会告警
prefetch(预加载未来资源)
- 语法
<link rel="prefetch" href="next-page.js" as="script" />
- 特点:
- 延迟加载:浏览器在空闲时下载,不阻塞当前页面渲染
- 跨页面缓存:预取的资源保留在 HTTP 缓存中,供后续页面使用
- 应用场景:
- 用户可能跳转的下一个页面的 css/js
- 未来交互所需要的资源(如弹窗图片)
- 注意事项
- 错误的预取会浪费带宽
- 浏览器可能因为网络条件或资源优先级取消预取
- 浏览器解析与渲染过程有哪些线程参与?分别负责什么?
参与的线程
- GUI 渲染线程
- 解析 HTML/CSS,构建 DOM 树 CSSOM 树、合并生成渲染树、完成布局和绘制
- JS 引擎线程
- 解析和执行 JS 代码、操作 DOM/CSSOM
- 事件触发线程
- 管理事件队列(如点击、异步请求完成等)
- 将事件回调加入任务队列,等待 js 引擎执行
- 定时器触发线程
- 处理
setTimeout
、setInterval
的计时任务,计时结束后将回调加入事件队列
- 处理
- 异步请求线程
- 处理网络请求(如 ajax),请求完成后通知事件触发线程,将回调加入队列
协助机制
- GUI 渲染线程于 JS 引擎线程互斥:JS 执行时,GUI 挂起,需避免长时间 JS 任务以优化首屏渲染速度
- 异步任务分线程处理:事件、定时器、网络请求等通过独立线程处理,减少主线程阻塞,提升页面响应性
- 绘制阶段进行图层分块处理的作用是什么?
- 可视区域优先:优先光栅化视口附件的图块(Viewport-aware)
- GPU 友好:符合显存纹理单元最近处理尺寸
- 内存优化:释放不可见图块内存(如滚动后移出视口的区域)
- 并行加速:多个图块可同时进行光栅化处理
总结
浏览器渲染流程是性能优化的核心战场。通过理解 DOM/CSSOM 构建、渲染树生成、布局与绘制的细节,开发者可针对性地减少阻塞、避免重排/重绘,并利用分层与合成技术提升交互流畅度。最终目标是为用户提供快速、稳定的视觉体验。