回流
回流又名重排,指 布局 或 几何属性
需改变的渲染。
渲染树的节点发生改变,影响了该节点的几何属性,导致该节点位置发生变化,此时就会触发浏览器回流并重新生成渲染树。
回流意味着节点的几何属性改变,需重新计算并生成渲染树,导致渲染树的全部或部分发生变化。
重绘
重绘是指当节点需要更改外观而不会影响布局的,比如改变 color
就叫称为重绘。
渲染树的节点发生改变,但不影响该节点的几何属性。
由此可见,回流对浏览器性能的消耗是高于重绘的,而且回流一定会伴随重绘,重绘却不一定伴随回流。
属性分类
查询属性渲染状态的网站 CssTriggers
- 几何属性:包括布局、尺寸等可用数学几何衡量的属性
- 布局:
display
、float
、position
、list
、table
、flex
、columns
、grid
- 尺寸:
margin
、padding
、border
、width
、height
- 布局:
- 外观属性:包括界面、文字等可用状态向量描述的属性
- 界面:
appearance
、outline
、background
、mask
、box-shadow
、box-reflect
、filter
、opacity
、clip
、transform
- 文字:
text
、font
、word
- 界面:
性能优化
回流重绘在操作节点样式时频繁出现,同时也存在很大程度上的性能问题。
回流必定引发重绘,重绘不一定引发回流,可利用该法则解决一些因为回流重绘而引发的性能问题。
会触发回流的情况:
- 改变窗口大小
- 修改盒模型
- 增删样式
- 重构布局
- 重设尺寸
- 改变字体
- 改动文字
使用visibility:hidden替换display:none
笔者从以下四方面对比display:none
和visibility:hidden
,display:none
简称DN
,visibility:hidden
简称VH
。
- 占位表现
- DN不占据空间
- VH占据空间
- 触发影响
- DN触发回流重绘
- VH触发重绘
- 过渡影响
- DN影响过渡不影响动画
- VH不影响过渡不影响动画
- 株连效果
- DN后自身及其子节点全都不可见
- VH后自身及其子节点全都不可见但可声明子节点
visibility:visible
单独显示
两者的占位表现
、触发影响
和株连效果
就能说明VH
代替DN
的好处,从两者区别中就能找出恰当的答案了。
使用transform代替top
top
是几何属性,操作top
会改变节点位置从而引发回流,使用transform:translate3d(x,0,0)
代替top
,只会引发图层重绘,还会间接启动GPU加速。
避免使用Table布局
牵一发而动全身
用在Table布局身上就很适合了,可能很小的一个改动就会造成整个<table>
回流,有兴趣的同学可用Chrome Devtools
的Performance
调试看看,在此就不演示了。
通常可用<ul>
、<li>
和<span>
等标签取代<table>
系列标签生成表格。
避免规则层级过多
浏览器的CSS解析器
解析css文件
时,对CSS规则是从右到左匹配查找,样式层级过多会影响回流重绘效率,建议保持CSS规则在3层
左右。
避免节点属性值放在循环里当成循环变量
JS 中 获取几何属性时也会触发回流 ( 获取属性值时,会重新计算获取最新的值,导致回流 )
现代的浏览器都是很聪明的,由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!当你获取布局信息的操作的时候,会强制队列刷新,比如当你访问以下属性或者使用以下方法:
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- getComputedStyle()
- getBoundingClientRect
- 具体可以访问这个网站:https://gist.github.com/pauli…点击预览
以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值缓存起来。
动态改变类而不改变样式 ( 最小化重绘和重排 )
不要尝试每次操作DOM去改变节点样式,这样会频繁触发回流。
更好的方式是使用新的类名预定义节点样式,在执行逻辑操作时收集并确认最终更换的类名集合,在适合时机一次性动态替换原来的类名集合。
使用cssText
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';
修改CSS的class
const el = document.getElementById('test');
el.className += ' active';