Skip to content

渲染机制

浏览器的渲染机制一般分为以下几个步骤:

  1. 处理HTML并构建DOM树
  2. 处理CSS构建CSSOM树
  3. 将DOM与CSSOM合并成一个渲染树
  4. 根据渲染树来布局,计算每个节点的位置
  5. 调用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有关:

  1. 当Event Loop执行完Microtask后,会判断document是否需要更新。因为浏览器是60Hz的刷新率,每16ms才会更新一次
  2. 然后判断是否有resize或scroll,有的话会触发事件,所以resize和scroll事件也是至少16ms才会触发一次,并且自带节流功能
  3. 判断是否触发了media query
  4. 更新动画并且发送事件
  5. 判断是否有全屏操作事件
  6. 执行requestAnimationFrame回调
  7. 执行IntersectionObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好
  8. 更新界面
  9. 以上就是一帧中可能会做的事。如果在一帧中有空闲时间,就会去执行requestIdleCallback回调

减少重绘和回流

  • 使用translate替代top left
  • 使用visibility替换display: none,前者只会引起重绘,后者会引发回流(改变了布局)
  • 把DOM离线后修改,比如:先把DOM给display:none(有一次reflow)然后进行各种修改再把它显示出来
  • 不要把DOM节点的属性值放在一个循环里当成循环里的变量
  • 不要使用table布局,很小的改动会造成整个table的重新布局
  • 动画实现的速度选择,动画速度越快,回流次数越多,也可以选择用requestAnimationFrame
  • CSS选择符从右往左匹配查找,避免DOM深度过深
  • 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于video标签,浏览器会自动将该节点变为图层

共 20 个模块,1301 篇 Markdown 文档。