重绘(repaint)和回流(reflow)
首先要知道,重排一定会引起重绘,重绘不一定引起重排
在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及回流和重绘:
回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置
重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制
回流的触发时机
回流这一阶段主要是计算节点的位置和几何信息,那么 当页面布局和几何信息发生变化的时候,导致浏览器重新计算元素的几何属性,就需要回流,重新构建渲染树
,有下面几种情况:
-
页面一开始渲染的时候,就是浏览器一开始解析的时候,避免不了
-
添加/删除DOM元素
-
元素位置/尺寸(margin,padding,border,height,width…)/内容发生变化
-
浏览器窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
-
获取一些特定属性的值的时候:
offsetTop
offsetLeft
offsetWidth
offsetHeight
scrollTop
scrollLeft
scrollWidth
scrollHeight
clientTop
clientLeft
clientWidth
clientHeight
上面这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流
重绘的触发时机
当元素的外观属性(如颜色、背景等)发生改变,但不影响布局时的重新绘制过程。
重绘不会影响元素的几何尺寸和位置。重排一定会引起重绘,重绘不一定引起重排
- 触发了回流时
- 当DOM修改导致了样式的变化,并且没有影响几何属性时
- 由于没有导致
DOM
几何属性的变化,因此元素的位置信息不需要更新,所以当发生重绘的时候,会跳过生成布局树
和建立图层树
的阶段,直接到生成绘制列表
,然后继续进行分块、生成位图等后面一系列操作
如何避免
-
避免频繁使用style,而是采用修改class的方式
const container = document.getElementById('container') container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red'
避免改变样式,使用class类名去合并样式
<style> .basic_style { width: 100px; height: 200px; border: 10px solid red; color: red; } </style> <script> const container = document.getElementById('container') container.classList.add('basic_style') </script>
-
批量操作
DOM
,比如读取某元素的offsetWidth属性存到一个临时变量再去使用,而不是频繁使用这个计算属性;利用document.createDocumentFragment()文档片段来添加要添加的节点,处理完之后再插入到实际DOM中 -
使用离线DOM,先为元素设置
display: none
,操作结束后再把它显示出来。因为在display
属性为none
的元素上进行的DOM操作不会引发回流和重绘 -
缓存布局属性值,避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
// 缓存offsetLeft与offsetTop的值 const el = document.getElementById('el') let offLeft = el.offsetLeft, offTop = el.offsetTop // 在JS层面进行计算 for(let i=0;i<10;i++) { offLeft += 10 offTop += 10 } // 一次性将计算结果应用到DOM上 el.style.left = offLeft + "px" el.style.top = offTop + "px"
-
使用 CSS3 动画,利用 CSS3 的
transform
、opacity
、filter
这些属性可以实现合成的效果,也就是CPU
加速,可以减少重绘和回流的影响。