We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:
HTML
具体的浏览器解析渲染机制如下所示:
在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变
当我们对DOM的修改引发了DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来
DOM
当我们对DOM的修改导致了样式的变化(color或background-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了回流
color
background-color
回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:
还有一些容易被忽略的操作:获取一些特定属性的值
offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流
触发回流一定会触发重绘
可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边移到了右边,那我们要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)
除此之外还有一些其他引起重绘行为:
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列
当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据
offsetTop
因此浏览器不得不清空队列,触发回流重绘来返回正确的值
我们了解了如何触发回流和重绘的场景,下面给出避免回流的经验:
class
position
table
position: fixed/absolute
transform
opacity
filters
JavaScript
在使用 JavaScript 动态插入多个节点时, 可以使用DocumentFragment。 创建后一次插入,就能避免多次的渲染性能
DocumentFragment
但有时候,我们会无可避免地进行回流或者重绘,我们可以更好使用它们
例如,多次修改一个把元素布局的时候,我们很可能会如下操作
const el = document.getElementById('el') for(let i=0;i<10;i++) { el.style.top = el.offsetTop + 10 + "px"; el.style.left = el.offsetLeft + 10 + "px"; }
每次循环都需要获取多次offset属性,比较糟糕,可以使用变量的形式缓存起来,待计算完毕再提交给浏览器发出重计算请求
offset
// 缓存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"
我们还可避免改变样式,使用类名去合并样式
const container = document.getElementById('container') container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red'
使用类名去合并样式
<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>
前者每次单独操作,都去触发一次渲染树更改(新浏览器不会),
都去触发一次渲染树更改,从而导致相应的回流与重绘过程
合并之后,等于我们将所有的更改一次性发出
我们还可以通过通过设置元素属性display: none,将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作
display: none
离线操作后
let container = document.getElementById('container') container.style.display = 'none' container.style.width = '100px' container.style.height = '200px' container.style.border = '10px solid red' container.style.color = 'red' ...(省略了许多类似的后续操作) container.style.display = 'block'
The text was updated successfully, but these errors were encountered:
No branches or pull requests
一、是什么
在
HTML
中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:具体的浏览器解析渲染机制如下所示:
在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变
当我们对
DOM
的修改引发了DOM
几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来当我们对
DOM
的修改导致了样式的变化(color
或background-color
),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了回流二、如何触发
回流触发时机
回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:
还有一些容易被忽略的操作:获取一些特定属性的值
这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流
重绘触发时机
触发回流一定会触发重绘
可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边移到了右边,那我们要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)
除此之外还有一些其他引起重绘行为:
三、浏览器优化机制
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列
当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的
offsetTop
等方法都会返回最新的数据因此浏览器不得不清空队列,触发回流重绘来返回正确的值
四、如何减少
我们了解了如何触发回流和重绘的场景,下面给出避免回流的经验:
class
类名 (尽可能在 DOM 树的最里层)position
属性的 fixed 值或 absolute 值(如前文示例所提)table
布局,table
中每个元素的大小以及内容的改动,都会导致整个table
的重新计算position: fixed/absolute
,尽可能地使元素脱离文档流,从而减少对其他元素的影响transform
、opacity
、filters
这些动画不会引起回流重绘JavaScript
表达式在使用 JavaScript 动态插入多个节点时, 可以使用
DocumentFragment
。 创建后一次插入,就能避免多次的渲染性能但有时候,我们会无可避免地进行回流或者重绘,我们可以更好使用它们
例如,多次修改一个把元素布局的时候,我们很可能会如下操作
每次循环都需要获取多次
offset
属性,比较糟糕,可以使用变量的形式缓存起来,待计算完毕再提交给浏览器发出重计算请求我们还可避免改变样式,使用类名去合并样式
使用类名去合并样式
前者每次单独操作,都去触发一次渲染树更改(新浏览器不会),
都去触发一次渲染树更改,从而导致相应的回流与重绘过程
合并之后,等于我们将所有的更改一次性发出
我们还可以通过通过设置元素属性
display: none
,将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作离线操作后
参考文献
The text was updated successfully, but these errors were encountered: