1 浏览器是如何解析 html, css , js 的?
浏览器底层是 cpp 写的,cpp 是多线程执行的,所以浏览器渲染页面的全过程是多线程的。来看看这张图:
这就是浏览器从资源接收开始,逐步拆解 HTML -> 可视化页面的完整渲染过程。
2 浏览器渲染页面全过程
2.1 资源加载:从网络到浏览器
-
网络请求
- 浏览器通过 HTTP/HTTPS 等协议,从服务器获取 HTML、CSS、JS、图片 等资源。
- 资源会被缓存,优先缓存加载已存在的文件。
-
资源解析优先级
- HTML:作为 “骨架”,是浏览器解析的起点,阻塞后续渲染(除非异步加载)。
- CSS:作为 “皮肤”,会阻塞渲染(需构建 CSSOM 才能计算样式),但可并行加载(多个 CSS 文件同时下载)。
- JS:作为 “交互逻辑”,会阻塞 HTML 解析(需执行完 JS 才继续解析),可通过
async/defer优化。
2.2 核心流程:HTML / CSS -> DOM / CSSOM 树 -> 渲染树 -> 可视化
2.2.1 构建 DOM 树(HTML → DOM Tree)
-
输入:HTML 字符串(网络传输后转成字符流)。
-
过程:
-
字节转换:将网络传输的二进制数据转为 UTF-8 等编码的字符。
-
令牌化(Tokenization):按 HTML 语法拆分字符为令牌(如标签名
<div>、属性id="box"、文本内容等)。 -
构建节点:将令牌转换为 DOM 节点对象(包含
type、attrs、children等属性)。 -
生成树结构:按嵌套关系拼接节点,形成DOM 树(根节点为
document),便于用数据结构进行树结构的查找等操作(bfs dfs)。 -
Eg:
-
<div id="box">Hello</div> -
令牌化:
-
{ type: 'div', attrs: { id: 'box' }, children: [ { type: 'text', content: 'Hello' } ] } -
2.2.2 构建 CSSOM 树(CSS → CSSOM Tree)
-
输入:CSS 规则(内联样式、
<style>、外部 CSS 文件)。 -
过程:
- 解析 CSS 规则:拆分选择器(如
div#box)、属性(如color: red)。 - 计算样式优先级:处理层叠(Cascade)、继承(Inheritance)、 specificity(权重),确定每个元素的最终样式。
- 生成 CSSOM 树:按选择器层级组织,形成CSS 对象模型(类似 DOM 树的结构,但只存储样式规则,还没渲染到页面)。
- 解析 CSS 规则:拆分选择器(如
-
关键特点:
-
CSSOM 是渲染阻塞资源:必须等 CSSOM 构建完成,才能进入 “布局” 阶段(否则无法计算元素位置)。
-
浏览器会自动补全默认样式(如
body { margin: 8px })。
-
-
2.2.3 构建渲染树(Render Tree)
-
输入:DOM 树 + CSSOM 树。
-
过程:
- 筛选可见节点:过滤
display: none、<head>中的元数据等不可见元素。 - 关联样式:为每个可见 DOM 节点匹配 CSSOM 中的样式规则,生成渲染节点(包含样式、位置等信息)。
- 生成渲染树:按可见节点的层级关系,拼接成渲染树(Render Tree)。
- 筛选可见节点:过滤
-
示例:
- DOM 树中有
<div id="box">,CSSOM 中有#box { color: red },则渲染树中该节点会携带color: red样式。
- DOM 树中有
2.2.4 布局(Layout):计算元素位置
-
输入:渲染树。
-
过程:
- 建立坐标系:以视口(Viewport)为基准,确定根节点的位置。
- 计算盒模型:遍历渲染树,计算每个节点的
width、height、margin、padding等(基于 CSS 盒模型)。 - 处理文档流:按
display(block/inline/flex等)规则,确定元素在页面中的坐标位置(如left、top)。 - 处理 BFC(块格式化上下文):解决浮动、 margin 折叠等问题,保证布局稳定。
-
关键触发条件:
- 首次渲染、窗口 resize、元素样式修改(如
width变化)会触发回流(Reflow),成本较高(需重新计算整个布局)。
- 首次渲染、窗口 resize、元素样式修改(如
2.2.5 分层(Layer):优化渲染性能
-
作用:将复杂页面拆分为多个图层,避免全局重绘 / 回流。
-
分层条件:
- 元素含
transform、opacity、position: fixed/absolute、z-index等属性。 - 视频、Canvas、WebGL 等独立内容。
- 元素含
-
原理: 浏览器为每个图层单独处理绘制和合成,利用 GPU加速(减少 CPU 压力)。
2.2.6 绘制(Painting):填充像素
-
输入:分层后的渲染树。
-
过程:
- 生成绘制指令:遍历每个图层,将样式(颜色、边框、阴影、文字等)转换为像素绘制指令。
- 光栅化(Rasterization):将矢量图形(如 CSS 样式)转换为位图(像素网格),适配屏幕分辨率。
- 处理合成层:不同图层的绘制结果会被暂存,等待最终合并。
-
关键触发条件: 修改不影响布局的样式(如
color、background)会触发重绘(Repaint),成本低于回流。
2.2.7 合成(Compositing):合并图层
-
输入:各图层的绘制结果。
-
过程:
-
确定层叠顺序:按
z-index等规则,确定图层的上下层级。 -
合成最终图像:通过合成线程将所有图层合并为一张完整的页面图像,输出到屏幕。
-
2.2.8 显示(Display):输出到屏幕
- 合成后的图像被提交到显示器,通过显卡、屏幕驱动等硬件,最终呈现为用户可见的页面。
3 动画与渲染流程的关联
3.1 CSS 动画(transition/animation)
-
触发时机:样式变化(如
left、transform)被检测到后,浏览器自动在渲染流程中插入 “过渡帧”。 -
原理:
- 浏览器在布局 / 绘制阶段,自动计算关键帧之间的样式插值(如从
left: 0到left: 300px的中间值)。 - 利用 GPU 加速(尤其是
transform/opacity,仅触发合成层更新,跳过回流 / 重绘)。
- 浏览器在布局 / 绘制阶段,自动计算关键帧之间的样式插值(如从
-
案例:我想实现这个红色盒子box 从 left:0; 移动到 left:300px; 的动画效果。
<style>
.box{
width: 100px;
height: 100px;
background-color: red;
position: relative;
left: 0;
top: 0;
transition: left 1s ease-out;/*需要检测到两个不同时间点的样式状态才触发*/
}
.active{
left: 300px;
}
</style>
<div class="box"></div>
<script>
document.querySelector('.box').classList.add('active');//刷新页面直接显示最后left:300px的样子。
</script>
这个结果并没有我想象的动画效果,why??
原因:在 DOM 树和 CSSOM树 构建完毕之后,
.box的初始样式(left:0)已生效,但还没有对页面进行渲染。执行到了JS(JS 代码在 DOM 解析完成后立即执行(因为 导致浏览器批量处理样式变化,跳过了 “过渡帧” 检测。而过渡动画需要检测到两个不同时间点的样式状态,但批量处理导致这两个状态被合并为一次更新。
3.2 JS 动画(requestAnimationFrame)
- 原理:
requestAnimationFrame(RAF)会在浏览器下一次重绘前执行回调。 - 案例:我想实现这个红色盒子box 从 left:0; 移动到 left:300px; 的动画效果。
<style>
.box{
width: 100px;
height: 100px;
background-color: red;
position: relative;
left: 0;
top: 0;
}
</style>
<div class="box"></div>
<script>
const box = document.querySelector('.box');
let width = 0;
function move(){
width += 2;
box.style.width = width + 'px';
if (width < 300) {
requestAnimationFrame(move); // requestAnimationFrame是浏览器提供的动画API
}
}
move();
</script>
成功实现。
4 关键事件与渲染时机
4.1 DOMContentLoaded
- 触发时机:DOM 树构建完成后(无需等待 CSS、图片加载)。
- 特点:此时渲染流程尚未完成(布局、绘制可能还在进行),但可操作 DOM。
4.2 load
- 触发时机:所有资源(CSS、JS、图片等)加载完成后。
- 特点:页面已完成首次渲染,可获取最终布局信息(如
offsetWidth)。
4.3 requestAnimationFrame
-
触发时机:下一次重绘前。
-
作用:保证动画帧与渲染流程对齐,避免丢帧。
5 回流与重构
5.1 回流
-
定义:当浏览器发现元素的几何属性(位置、尺寸等)发生变化,或者触发了某些会影响布局的操作时,需要重新计算元素在文档流中的位置和大小,以及对其他元素的影响,然后再重新构建渲染树、重新布局页面,这个过程被称为回流。
-
触发条件:修改元素尺寸、改变元素位置、添加或删除可见的DOM元素、修改字体大小、改变浏览器窗口大小、激活CSS伪类(
:hover、:active等)。 -
对性能的影响:回流的开销比重绘大很多,因为它涉及到重新计算元素的位置和大小,以及对整个页面布局的重新构建。
5.2 重构
- 定义:当元素的外观(样式)发生改变,但不会影响其在文档流中的位置和布局(几何属性)时,浏览器会将新样式应用到该元素上,并重新绘制它,这个过程被称为重绘。
- 触发条件:改变元素的颜色、修改背景属性、更改边框样式、改变阴影。
- 对性能的影响:虽然重绘会消耗一定的性能,因为浏览器需要重新计算元素的样式并绘制到屏幕上,但相对回流而言,重绘的开销较小。