文章目录


从上面这个图上,我们可以看到,浏览器渲染过程如下:
- 解析HTML,生成DOM树,解析CSS,生成CSSOM树
- 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
- Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
- Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
- Display:将像素发送给GPU,展示在页面上。这一步其实还有很多内容,比如会在GPU将多个合成层合并为同一个层,并展示在页面中
0. 浏览器控制台关键渲染步骤名词解释
步骤 | 是否直接影响用户所见 | 说明 |
---|---|---|
样式计算 (Recalculate Style) | ❌ 间接影响 | 确定CSS规则,但不直接绘制 |
布局 (Layout/Reflow) | ❌ 间接影响 | 计算元素位置,不直接可见 |
绘制 (Paint) | ❌ 间接影响 | 生成绘制指令,未上屏 |
分层 (Layer) | ❌ 间接影响 | 优化绘制结构,不可见 |
合成 (Composite) | ✅ 直接影响 | 将各图层合并为最终画面 |
显示 (Display) | ✅ 直接影响 | 将像素输出到屏幕 |
1. 回流(Reflow)
回流也称为布局(Layout),是指浏览器计算网页中元素的
位置
和几何信息
的过程。当页面布局
或元素的几何属性
发生变化时,浏览器需要重新计算元素的位置和大小
,这个过程称为回流
。
1.1 触发回流的操作
- 添加或删除可见的DOM元素
- 页面初始渲染(无可避免,这个是开销最大的一次)
- 浏览器窗口大小改变(resize事件)
- 内容变化(如文本改变或图片大小改变)
- 激活CSS伪类(如:hover)
- 计算offsetWidth、offsetHeight等布局属性(需要返回
最新的布局信息
,因此浏览器不得不触发回流重绘来返回正确的值) - 元素尺寸或位置发生改变(边距、宽高、边框等)
1.2 引起回流的属性
1.3 演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="app"></div>
<button>改变</button>
<script>
let btnEl = document.querySelector('button');
function fn() {
let appEl = document.querySelector('#app');
appEl.style.width = '300px';
}
// 为了方便看,第二个参数,用一个变量传递
btnEl.addEventListener('click', fn)
</script>
</body>
</html>
可以看到,修改这个宽度,会触发回流,触发 layout 布局 操作,这里的layout 就是指
回流
2. 重绘(Repaint)
重绘是指当元素的
外观样式(如颜色、背景、边框等)
发生变化,但不影响布局时,浏览器只需要重新绘制受影响的部分
,而不需要重新计算布局
。
2.1 触发重绘的操作
- 改变元素的颜色、背景色、透明度等视觉样式
- 改变元素的边框样式
- 改变文本样式(如字体、字号、颜色等)
- 显示/隐藏元素(不改变布局的情况下)
2.2 演示
可以看到这个地方,就是重绘执行的效果,
3. 两者的关系
回流必定会引起重绘
:因为元素位置变化后,需要重新绘制重绘不一定引起回流
:如果只是样式变化不影响布局,则只需重绘
4. 实际操作
4.1 在点击事件里面修改宽和高,会触发几次回流
其实会触发
一次 回流
,这里浏览器是有优化的,为了提高效率
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app {
width: 200px;
height: 200px;
background-color: red;
}
</style>
</head>
<body>
<div id="app"></div>
<button>改变</button>
<script>
let btnEl = document.querySelector('button');
function fn() {
let appEl = document.querySelector('#app');
appEl.style.width = '300px';
appEl.style.height = '400px';
}
// 为了方便看,第二个参数,用一个变量传递
btnEl.addEventListener('click', fn)
</script>
</body>
</html>