关于回流与重绘优化的探索

前言

杭州下雪了,冷到不行,在家躺在床上玩手机,打开微信进入前端交流群里日常吹水,看到大佬在群里发了一篇文章你应该要知道的重绘与重排,文章里有一段骚操作,就是为了减少重绘与重排,合并样式操作,这个骚操作成功的引起了我的注意,然后开启了我的探索。

正文

前言中描述的合并样式的骚操作是如下:

var el = document.querySelector('div');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';

原文描述的大概意思是这段代码多次对 DOM 的修改和对样式的修改,页面会进行多次回流或者重绘,应该进行如下优化:

var el = document.querySelector('div');
el.style.cssText = 'border-left: 1px; border-right: 1px; padding: 5px;'

这样的优化在以前我刚开始学习前端的时候,经常也在一些相关的性能优化的文章里看到,因为一直没有探究过,概念里一直觉得自己应该把多次 DOM 的样式的修改合并在一起,这样效率会更高,直到后来,自己对浏览器的进程与线程慢慢有了了解,曾经也写过一篇博客,浅谈浏览器多进程与JS线程,其中有一个概念是,JS线程与GUI渲染线程是互斥关系,大概的意思就是当js引擎在执行js代码的时候,浏览器的渲染引擎是被冻结了的,无法渲染页面的,必须等待js引擎空闲了才能渲染页面。

这个概念,JS线程与GUI渲染线程是互斥关系与上面描述的骚操作似乎有点冲突,也就是当我们对el.style进行一系列赋值的时候,渲染引擎是被冻结的状态,怎么会进行多次重绘或者回流?带着这样的疑问,写了一个小demo,代码如下。

<!DOCTYPE html>
<html>
<head>
  <title>测试页</title>
  <style>
    #box {
      width: 109px;
      height: 100px;
      background-color: lightsteelblue;
      border-style: solid;
    }
  </style>
</head>
<body>
  <div id="box"></div>
</body>
<script>
var box = document.getElementById('box');
var toggle = 0;
var time = 500;
function toggleFun() {
  var borderWidth = toggle ? 20 : 0;
  var borderColor = toggle ? 'coral' : 'transparent';
  if (toggle) {
    box.style.borderWidth = '50px';
    box.style.borderWidth = borderWidth + 'px';
    box.style.borderColor = borderColor;
  } else {
    box.style.cssText = 'border: ' + borderWidth + 'px solid' + borderColor;
  }
  toggle = toggle ? 0 : 1;
}
setInterval(toggleFun, time)
</script>
</html>

代码大概的意思就是定时以两种操作设置样式,收集浏览器的回流或者重绘次数。

打开chrome的开发者工具,切换到Performance选项卡,点击左上角的圆 ○,开始record,等几秒后stop,点击Frames查看Event log选项卡,内容如下:

图片描述

大概可以看到,Recalculate Style -> Layout -> Update Layer Tree -> Paint -> Composite Layers 这个过程在循环进行,触发的目标代码是第25行代码合29行代码,也就是box.style.borderWidth = '50px';box.style.cssText = 'border: ' + borderWidth + 'px solid' + borderColor;

首先回顾一下浏览器渲染页面的流程:

  1. 请求拿到html报文。
  2. 同时解析生成CSS规则树和DOM树。
  3. 合并CSS规则树和DOM树,生成render树。
  4. 渲染进程根据render树进行Layout。
  5. 绘制paint页面。

然后在看看上面的过程,可以容易看出,

  1. 首先,Recalculate Style,重新计算css规则树。
  2. 进行Layout,这里的Layout可以理解成回流,重新计算每个元素的位置。
  3. Update Layer Tree,字面意思理解,更新层级树。
  4. Paint,绘制页面,在这里可以理解成重绘。
  5. Composite Layers,字面意思理解,合并层级。

由上面过程得到结果,当在同一执行任务里面对DOM的样式进行多次操作的时候,只会进行一次回流或者重绘,也就是说,只要我们的js引擎时候忙碌的,渲染引擎是冻结的时候,无论对DOM样式进行多少次操作,都只会进行一次回流或者重绘,也就是说前面说的合并样式优化是无效的。

这个时候,我对上面过程又产生了新的疑问,为什么要Paint之后在Composite Layers呢?为什么不把所有层合并完了在绘制页面呢?

.........................(看搜索相关资料去了)

翻看资料结束后,我得到以下理解。

首先理解layer概念,可以理解成PS里面的图层,我们知道PS文件最后保存层PSD文件,当图层越多的时候,PSD文件就越大,在我们的浏览器里面也是一样的,我们的layer越多,所占的内存就越大。

然后理解Paint真正做的事情,paint的任务大概就是把所有的layer绘制到页面中,这个绘制与canvas的绘制不一样,canvas的绘制相当于在画布里把像素直接绘制成指定颜色,然后我们直接看到的东西就直接是像素颜色,而我们这里说的Paint只是把图层丢到页面中,最后的绘制,需要交给Composite线程处理。

最后是Composite Layers,由composite线程进行,这个线程在浏览器的Renderer进程中,任务是把Paint时候丢上页面的图层转化成位图,最终生成我们肉眼可以看到的图像,所以,真正的绘制,应该是Composite Layers过程进行的。

由于paintcomposite解耦,浏览器对每一个layer都有一个标识,这个标识用来标识该layer是否需要重绘,在有CSS规则树变化的时候,浏览器只会对这些被标识的layer进行重绘,用这样的方式提高浏览器的渲染性能。

最后

前端大法博大精深,越往下学越觉得自己不适合前端!!!仿佛看到自己在从入门到跑路这条路上快走到了终点。。。

参考


麦乐
菜鸟历程
3.8k 声望
955 粉丝
0 条评论
推荐阅读
深入理解Chrome V8垃圾回收机制
最近,项目进入维护期,基本没有什么需求,比较闲,这让我莫名的有了危机感,每天像是在混日子,感觉这像是在温水煮青蛙,已经毕业3年了,很怕自己到了5年经验的时候,能力却和3年经验的时候一样,没什么长进。于...

_杨溜溜38阅读 7.9k评论 3

浏览器 Notification 桌面推送通知
🎈 什么是 NotificationNotification 是浏览器最小化后在桌面显示消息的一种方法类似于 360 等流氓软件在桌面右下角的弹窗广告它与浏览器是脱离的,消息是置顶的🎈 弹窗授权授权当前页面允许通知可以通过检查只读属...

tiny极客3阅读 981评论 2

封面图
《图解 Google V8》设计思想篇——学习笔记(一)
这个专栏的优点是:写的通俗易懂,没有涉及 V8 源码部分,对于前端还是比较友好的,学完之后能够知道写下一段 js 代码后,V8 背后都做了哪些事情

uccs1阅读 511评论 1

封面图
分布式数据库--SQL优化之Plan Hint
Hint是嵌入SQL语句的对优化器进行提示的信息,是DBA进行SQL优化的常用手段。SQL语句经过优化器(规则优化(RBO)、代价优化(CBO)),通常会选择正确的查询路径,但是智者千虑,必有一失,有时优化器也会选择一个很差...

KaiwuDB1阅读 701

封面图
深入解析 Raft 模块在开务数据库中的优化改造(下)
开务数据库是由浪潮开源的一款 NewSQL 分布式数据库,具备 HTAP 特性,拥有强一致、高可用的分布式架构。其中,开务数据库各方面的强一致性都依靠 Raft 算法实现。我们在上一篇文章中介绍了 Raft 一致性算法在分...

KaiwuDB1阅读 1k

封面图
VisBug:助力前端开发的浏览器插件
作为前端开发者相信肯定有遇到过以下场景:看到一个网站某个模块不错的样式效果想要看CSS代码开发过程中调整样式,如字符溢出,对齐,字体大小颜色之类更改网站数据或样式截图分享在开发过程中,我们通常会遇到需...

南城FE2阅读 294评论 1

封面图
深入解析分布式数据库的 SQL 引擎优化
开务数据库的 SQL 引擎包含连接、编译、缓存、分布式日志和分布式执行五大服务组件,实现了多集群多节点协同的高效计算,大大提升了用户的查询效率。

KaiwuDB阅读 969

封面图
3.8k 声望
955 粉丝
宣传栏