初识渲染过程
预备知识
- 浏览器的渲染流程
- 浏览器DOM是分层的,网页是3D
- 对DOM元素节点计算样式结果 Recalculate Style样式重计算
- 为每个节点生成图形位置layout回流重排
- 将每个节点绘制填充到图层中Paint
- 图层作为纹理上传到GPU
- Composite Layers合成层把符合图层生成到页面
- Compisite Layers做了什么?
- 图层的绘制列表准备好,主线程commit合成线程
- 合成线程viewport rt划分图块
- 生成位图 光栅化
- 所有图块 GUP合成生成DrawQuad提交给浏览器渲染进程
- viz组件 接收到DrawQuad 绘制到屏幕上
页面分层
🤔
那些属性会分层?如何查看图层?
在performance里面点击Layers可以看到图层的列表:
- 根元素
- position
- transform
- 半透明
- css滤镜
- canvas
- video
- overflow
页面光分层不行,让 GPU 参与工作才是提升性能的王道,那些属性能让 GPU 参与工作呢?
GPU硬件加速
- CSS3D
- video
- webgl
- transform
- will-change:transform
影响重绘和重排的操作
重绘 是我们改动元素的字体颜色,背景色等外观元素时候,并不会改变它的大小,也不会影响其他元素的布局,这个时候就不需要重新构建渲染树。浏览器会对元素的样式重新绘制,这个过程叫做 重绘
重绘的触发: 任何对元素样式,如 color,background-color 等属性的改变,JS和CSS都会引起重绘。
重排:一般来说盒子动了必定必定会 重排,因此建议使用怪异盒模型,固定盒子的大小.
重排必定引起重绘
- 添加/删除元素
- 内容发生改变(文字数量或图片大小等等)
- display:none
- 移动元素位置
- 修改浏览器大小,字体大小
还有一些读取操作也可能引起重排
- offset(Top|Left|Width|Height)
- scroll(Top|Left|Width|Height)
- client(Top|Left|Width|Height)
- getComputedStyle()
这里有个疑问🤔️
为什么读取也会造成重排?
因为在JS操作DOM时,浏览器做了性能优化,将所有操作维护在一个队列中,当达到一定的数量浏览器再统一去执行队列中的操作,当需要去查询这些属性时,浏览器只能放弃优惠啊强制刷新队列,因为如果不立即执行队列中的任务,可能会得到过期的结果,所以浏览器必须打断优化流程引起重排。
csstriggers可以查看哪些属性会引起重绘和重排
性能优化-减少重绘
利用 DevTools 识别 paint 的瓶颈,Chrome 工具的使用:

蓝色:网络通信和HTML解析 黄色:JavaScript执行时间 紫色:样式计算和布局,即Layout重排 绿色:Paint重绘
CSS是否引起重绘和重排
测试代码如下:
<!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>
/* css in js */
/* :root{
--test:()=>{
console.log(123);
}
} */
.container {
position: relative;
min-height: 400px;
}
.ball {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
border-radius: 50%;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.75);
}
.ball-running {
animation: run-around 4s infinite;
}
@keyframes run-around {
0% {
/* top: 0;
left: 0; */
transform: translate(0, 0);
}
25% {
/* top: 0;
left: 200px; */
transform: translate(200px, 0);
}
50% {
/* top: 200px;
left: 200px; */
transform: translate(200px, 200px);
}
75% {
/* top: 200px;
left: 0; */
transform: translate(0, 200px);
}
}
</style>
</head>
<body>
<div class="container">
<div class="ball" id="ball"></div>
</div>
<script>
var balls = document.getElementById('ball');
balls.classList.add('ball');
balls.classList.add('ball-running');
</script>
</body>
</html>
打开performance录制

发现一直在执行Layout(重排)-Paint(重绘)-Composite Layers(合成)的过程
使用transform开启硬件加速,再次录制如下:

可以清楚的看到主线程现在处于空闲状态,而 GPU 参与了工作,直接跳过了重绘和重排的过程,大大的提升了性能!
小结
使用 top left会不断的重绘和重排,使用transform则不会
使用fastdom
在fastdom示例中我们可以明确的感觉到用了fastdom页面流畅了很多
如下写一个简单的fastdom案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>fastdom读写分离</title>
<style>
div {
display: flex;
margin: 20px;
}
div img {
height: 375px;
padding: 10px;
}
</style>
</head>
<body>
<div>
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
</div>
<div>
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
</div>
<div>
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
<img src="https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg">
</div>
<script>
const update = (times) => {
let imgs = document.getElementsByTagName('img')
for (let i = 0; i < imgs.length; i++) {
imgs[i].style.width = ((Math.sin(imgs[i].offsetTop + times / 1000) + 1) * 500) + 'px'
}
window.requestAnimationFrame(update)
}
window.requestAnimationFrame(update)
</script>
</body>
</html>
点开performance,查看页面绘制过程发现不停的 Recalculate 和进行 layout

但是使用 fastdom (opens new window)进行读写分离后,发现进行了统一的操作,大大减少了浏览器的开销

requestAnimationFrame
如果想执行动画操作,可以使用 requestAnimationFrame 要求浏览器在下次重绘之前调用指定的回调函数更新动画,可以达到和浏览器同步刷新,以避免不必要的开销。
总结

需要能在使用 css 3d硬件加速的地方尽量使用,以此优化 dom 的性能,极大的减少 dom 的重绘重排。也可以将读写进行分离以提高性能。