【前端基础】回流与重绘

 
 

基本概念

DOM Tree

浏览器会将html文本解析成DOM Tree,DOM Tree的每一个节点对应着一个dom元素。DOM Tree中包含了所有的DOM节点,包括display:none<head>

CSSOM

浏览器会将用户编写的css(包括cdn)浏览器设置的css解析成一个样式结构体,既CSSOM。

Render Tree

浏览器会使用DOM Tree与CSSOM构建出一个Render Tree,Render Tree的结构与DOM Tree基本相同,只是Render Tree中只包含用于呈现的节点以及对呈现有影响的节点。 所以Render Tree中必然不会包括display:none的节点和<head>节点。但是要注意,visibily:hidden的节点是会被包含在Render Tree中的,虽然这类节点本身不会被呈现出来,但是它们自身是有宽高的,所以是占一定空间的,因此是对呈现有影响的

Render Tree的构建过程类似于下面这样:

  1. 从DOM Tree中取出一个节点,判断它对呈现有无影响,如果无影响则忽略。
  2. 以从右到左的顺序选取此节点上面的选择器名称,去CSSOM里面查找对应的样式。
  3. 将节点信息和样式结合,生成Render Tree节点。
  4. 递归遍历整个DOM Tree最终生成Render Tree。

重绘与回流

重绘

用户看到的页面上的这些内容就是根据Render Tree去绘制出来的,所以如果我们修改了某个样式,比如字体颜色或背景颜色,那Render Tree中的节点信息就被我们修改了,此时必然是需要根据Render Tree中的节点信息重新进行绘制的。这便是重绘。
但是需要注意的是,这部分修改的属性是不影响页面布局的,只影响风格。否则就是回流了。

回流

如果我们修改了某个节点的宽高,那么这个节点之后的全部同级的节点,包括其全部子节点的属性都会受到影响,因为它们在页面中的位置发生了变化。此时浏览器就要让RenderTree中所有受影响的节点全部失效,然后重新构建这部分节点。这个过程就叫做回流。回流结束后浏览器还要对所有重新构建的节点进行重绘
同样的,修改某元素的字体大小,边框宽度,内外边距等都会引起回流,因为这些属性都影响了布局。

区别

回流结束后必定触发重绘,而且回流操作影响的节点非常多,所以回流带来的性能损耗是远远大于重绘的。

何时触发

重绘

  • 回流结束后必然触发重绘。
  • 对节点的风格做修改的时候会触发重绘。

回流

  • 页面首次渲染的时候必然会发生回流,因为这次首次构建Render Tree。
  • DOM修改。比如增删元素。
  • Render Tree修改。比如改变某节点的宽高,内外边距等。
  • js访问或设置元素的布局属性。比如dom.offsetTop、dom.scrollTop、dom.clientTop。
  • 浏览器创建resize。比如用户拖拽改变窗口大小。

优化策略

浏览器

说我们自己的优化策略之前,必须说一下浏览器它的优化策略。
浏览器维护了一个操作的缓存队列,当我们引发回流或重构的时候,浏览器不会立刻执行它们,而是将这些操作放到缓存队列中,一旦队列达到一定长度或者距离上次更新过去了足够长的时间,那么浏览器就会一次性执行这个队列中的全部操作(称为flush)。然后统一进行一次回流或重绘。

自己的

布局属性

  • 当我们访问或设置offsetTop/Width/Height等属性的时候,浏览器会将一次回流操作加入队列。但如果,在这个队列flush之前我们又访问了同一个布局属性的话,那浏览器为了确保我们拿到的属性是最新的,就会强制进行flush,然后将最新的属性返回给我们。所以如果我们连续频繁的访问布局属性的话,就相当于浏览器的优化策略失效了。
    于是,如果我们需要频繁的访问和修改某个布局属性的话,应该把它的值保存到某个变量内,然后对这个变量进行操作,全部操作结束后,再使用这个变量的值去更新相应的布局属性。

    反例
    myElement.style.left = 1 + myElement.offsetLeft + ‘px’
    myElement.style.top = 1 + myElement.offsetTop + ‘px’

    正例
    var current = myElement.offsetLeft
    myElement.style.left = current + ‘px’
    myElement.style.top = current + ‘px’

样式

  • 和布局属性一样的道理,如果我们连续修改某个元素的样式的话,那每次修改都会引起回流或重绘。我们应该一次性设置全部的样式。

    反例
    dom.style.color = “blue”;
    dom.style.backgroundColor= “white”;
    正例
    dom.setAttribute(“style”, “color:blue; background-color: white;”);

  • 如果某个节点有多种风格的话,我们可以通过切换class来实现,这也是一次修改全部样式的方法。和上面道理一样。

动画

  • 做动画应该使用transfrom和opacity,而不是通过连续修改元素的定位实现。因为transfrom只会引起重绘,而修改定位会引起回流。
  • 如果需要对一个节点使用动画的话,可以将这个节点脱离文档流,这样回流时候受影响的只是这个节点及其子节点,它对文档流中节点不会产生任何影响。等动画结束再将其加入到文档流中。这样的话,只会引起两次大面积回流操作,一次在移除文档流的时候,一次在加入回文档流的时候,这比起每帧动画都引起大面积回流要强多了。

参考

回流、重绘及其优化 https://segmentfault.com/a/1190000014474575
JavaScript——浏览器的重绘与回流 https://blog.csdn.net/qq_42269433/article/details/81133772
什么是回流,什么是重绘,有什么区别? https://www.jianshu.com/p/e081f9aa03fb

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值