高效前端之再探页面渲染优化

2021-4-2    前端达人

首先仍然不得不提的是 “在客户端拿到 HTML 后的处理”:

  • 从上到下解析 HTML 文档生成DOM树;
  • 加载解析样式构建CSSOM树;
  • 加载并执行JavaScript代码;
  • 根据DOM树和CSSOM树,生成 render 树;
  • 渲染;
  • 布局;
  • 绘制

我们可能很多次听到过:“要尽可能地减少重排和重绘,因为它们会影响浏览器性能。”
但,为什么呢?

事实上,一个页面是由许多层级组成的(就像千层饼一样) —— 这里的“层级”指的是“ DOM 元素渲染层(Layer)”。一个页面在构建完 render tree 到展现在我们面前还经历了一个“特别的流程”:

  1. 浏览器会先获取DOM树并依据样式将其分割成多个独立的渲染层
  2. CPU 将每一层绘制进位图中
  3. 将位图作为纹理上传至 GPU(显卡)绘制
  4. GPU 将所有的渲染层缓存并复合多个渲染层最终形成我们的图像(如果下次上传的渲染层没有发生变化,GPU 就不需要对其进行重新绘制)

(:从上面的步骤我们可以知道:布局是CPU处理的,而绘制是由GPU完成的
就像这张图说的(from Firefox的3D View插件的页面Layers层级图)
layer-index

问题就发生在上面所说流程的第2、4步中。大家试想一下:如果我们把那些会发生复杂运动/变化或一直发生大量重排重绘的元素提起出来,单独放在一个渲染层触发,那它就不会连累其他元素了!

那什么情况下会触发渲染层呢?
比如 video 、WebGL 、Canvas 、CSS3 3D 、CSS滤镜 、z-index大于某个相邻节点的值 的元素都会触发新的Layer —— 这里要理解一点:它并不单单指 z-index!这里极力推荐张鑫旭大大的这一篇文章:深入理解CSS中的层叠上下文和层叠顺序
比较简单的方法是,给元素加上下面的样式:

transform: translateZ(0); backface-visibility: hidden; 
  • 1
  • 2

我们把容易触发重排重绘的元素单独触发渲染层,让它与那些“静态”元素隔离,让 GPU 分担更多的渲染工作,我们通常把这样的措施成为硬件加速,或者是 GPU 加速。大家之前肯定听过这个说法 —— 就比如CSS中的 will-change 。

不论是重排还是重绘,都会阻塞浏览器。要提高网页性能,就要降低重排和重绘的频率和成本,近可能少地触发重新渲染。正如我们上面提到的:重排是由 CPU 处理的,而重绘是由 GPU 处理的,CPU 的处理效率远不及 GPU,并且重排一定会引发重绘,而重绘不一定会引发重排。所以在性能优化工作中,我们更应当着重减少重排的发生。


还有什么可以优化的?

  1. CSS 属性读写分离:浏览器没次对元素样式进行读操作时,都必须进行一次重新渲染(重排 + 重绘),所以我们在使用 JS 对元素样式进行读写操作时,最好将两者分离开,先读后写,避免出现两者交叉使用的情况
  2. 通过切换 class 或者 style.csstext 属性去批量操作元素样式
  3. DOM 元素离线更新:当对 DOM 进行相关操作时,例、appendChild 等都可以使用 documentFragment 对象进行离屏操作,带元素“组装”完成后再一次插入页面,或者使用 display:none 对元素隐藏,在元素“消失”后进行相关操作,然后再显示出来
  4. visibility: hidden 是个好东西,它既有display的隐藏,又有opacity的占位。而且它还支持移动动画
  5. 图片在渲染前指定大小:因为 img 元素是内联元素,所以在加载图片后会改变宽高,严重的情况会导致整个页面重排,所以最好在渲染前就指定其大小,或者让其脱离文档流

分享本文至:

日历

链接

个人资料

蓝蓝 http://www.lanlanwork.com

存档