渲染机制
浏览器的渲染机制一般分为以下几个步骤:
- 处理HTML并构建DOM树
- 处理CSS构建CSSOM树
- 将DOM与CSSOM合并成一个渲染树
- 根据渲染树来布局,计算每个节点的位置
- 调用GPU渲染,合成图层,显示在屏幕上
在构建CSSOM树时,会阻塞渲染,直到CSSOM树构建完成。并且构建CSSOM树是一个十分消耗性能的过程,所以应该尽量保证层级扁平,减少过度层叠,越是具体的CSS选择器,执行速度越慢
当HTML解析到script标签时,会暂停构建DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载JS文件。并且CSS也会影响JS的执行,只有当解析完样式表才会执行JS,所以也可以认为这种情况下,CSS也会暂停构建DOM
load 和 DOMContentLoaded 的区别
load事件触发代表页面中的DOM、CSS、JS、图片已经全部加载完毕
DOMContentLoaded事件触发代表初始的HTML被完全加载和解析,不需要等待CSS、JS和图片加载
图层
一般来说,可以把普通文档流看成一个图层,特定的属性可以生成一个新的图层。不同的图层渲染互不影响,所以对于某些频繁需要渲染的建议单独生成一个图层,提高性能。但也不能生成过多的图层,会引起反作用。
通过以下几个常用属性可以生成图层:
- 3D变换 translate3D translateZ
- will-change
- video iframe 标签
- 通过动画实现的opacity动画转换
- position: fixed
重绘Repaint 和 回流Reflow
重绘和回流是渲染步骤中的一小节,但是这两步骤对于性能影响很大。
- 重绘是当节点需要更改外观而不影响布局的,比如改变color就称为重绘
- 回流是布局或者几何属性需要改变就称为回流
回流必定发生重绘,重绘不一定引起回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。
所以以下几个动作可能导致性能问题:
- 改变window大小
- 改变字体
- 添加或删除样式
- 文字改变
- 定位或者浮动
- 盒模型
重绘和回流其实和Event Loop有关:
- 当Event Loop执行完Microtask后,会判断document是否需要更新。因为浏览器是60Hz的刷新率,每16ms才会更新一次
- 然后判断是否有resize或scroll,有的话会触发事件,所以resize和scroll事件也是至少16ms才会触发一次,并且自带节流功能
- 判断是否触发了media query
- 更新动画并且发送事件
- 判断是否有全屏操作事件
- 执行requestAnimationFrame回调
- 执行IntersectionObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好
- 更新界面
- 以上就是一帧中可能会做的事。如果在一帧中有空闲时间,就会去执行requestIdleCallback回调
减少重绘和回流
- 使用translate替代top left
- 使用visibility替换display: none,前者只会引起重绘,后者会引发回流(改变了布局)
- 把DOM离线后修改,比如:先把DOM给display:none(有一次reflow)然后进行各种修改再把它显示出来
- 不要把DOM节点的属性值放在一个循环里当成循环里的变量
- 不要使用table布局,很小的改动会造成整个table的重新布局
- 动画实现的速度选择,动画速度越快,回流次数越多,也可以选择用requestAnimationFrame
- CSS选择符从右往左匹配查找,避免DOM深度过深
- 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于video标签,浏览器会自动将该节点变为图层
