Skip to main content

初识渲染过程

预备知识
  • 浏览器的渲染流程
    1. 浏览器DOM是分层的,网页是3D
    2. 对DOM元素节点计算样式结果 Recalculate Style样式重计算
    3. 为每个节点生成图形位置layout回流重排
    4. 将每个节点绘制填充到图层中Paint
    5. 图层作为纹理上传到GPU
    6. Composite Layers合成层把符合图层生成到页面
  • Compisite Layers做了什么?
    1. 图层的绘制列表准备好,主线程commit合成线程
    2. 合成线程viewport rt划分图块
    3. 生成位图 光栅化
    4. 所有图块 GUP合成生成DrawQuad提交给浏览器渲染进程
    5. 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 工具的使用:

Locale Dropdown

蓝色:网络通信和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录制

Locale Dropdown

发现一直在执行Layout(重排)-Paint(重绘)-Composite Layers(合成)的过程

使用transform开启硬件加速,再次录制如下:

Locale Dropdown

可以清楚的看到主线程现在处于空闲状态,而 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

Locale Dropdown

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

Locale Dropdown

requestAnimationFrame

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

总结

Locale Dropdown

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