
回流 和 重绘 可以说是每一个web前端开发者经常听到的两个名词,虽然听的多,但是我们真的都理解它们的意思了嘛? 很显然都迷迷糊糊,当然在之前没有去了解的时候,小温也不例外! 之前是介于之前公司要分享,所以当时有做了解,所以这次打算和大伙分享一下!
🧐 预先剧透一下: "
回流" 和 “重绘” 是阻碍浏览器渲染 及 服务性能的关键因素中 比较重要的两点, 合理的规避它们,能够有效的提高项目的性能!
👉 一、回流 和 重绘 简介
讲到 回流 和 重绘,自然要从浏览器渲染讲起!为什么呢? 我们要知道,回流 和 重绘不可能单独存在,它们只可能存在于一个完整的步骤或者过程中,而这个过程就是 “ 浏览器渲染的过程 ”。接下来,为了进一步了解 回流 和 重绘,需要先了解浏览器渲染的过程,如下:
从上面这个图上,我们可以看到,浏览器渲染过程如下:
解析HTML,生成DOM树,解析CSS,生成CSSOM树将DOM树和CSSOM树结合,生成渲染树(Render Tree)Layout( 回流 ): 根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小);Painting( 重绘 ):根据渲染树以及回流得到的几何信息,得到节点的绝对像素;Display:将像素发送给GPU,展示在页面上。
在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变
当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来。这叫做 回流
当我们对 DOM 的修改导致了样式的变化(color或background-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了 重绘。
👉 二、如何合理减少回流 及 重绘 > 如何触发回流、重绘根据上面的内容,可以知道:重绘在回流之后,这样一个渲染顺序导致在渲染时,如果出现重绘,它不一定会导致回流,但是如果出现了回流就一定会导致重绘。
要想减少回流和重绘的次数,首先要了解回流和重绘是如何触发的,接下来,详细了解下,它们是如何触发的。
① 回流触发时机
回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:
还有一些容易被忽略的 *** 作:获取一些特定属性的值
offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流
除此还包括getComputedStyle 方法,原理是一样的
② 重绘触发时机
触发回流一定会触发重绘
可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边移到了右边,那我们要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)
除此之外还有一些其他引起重绘行为:
颜色的修改
文本方向的修改
阴影的修改
③ 浏览器优化机制
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改 *** 作放入到队列里,直到过了一段时间或者 *** 作达到了一个阈值,才清空队列
当你获取布局信息的 *** 作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据
因此浏览器不得不清空队列,触发回流重绘来返回正确的值。
> 如何减少回流 和 重绘在上面的内容中,我们了解到了如何触发回流和重绘的场景,为此,只需要按照相反的内容做出预防,即可减少回流和重绘,从而减少性能的损耗了。
下面给出避免回流的经验:
如果想设定元素的样式,通过改变元素的class 类名 (尽可能在 DOM 树的最里层)避免设置多项内联样式应用元素的动画,使用 position 属性的 fixed 值或 absolute 值(如前文示例所提)避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算对于那些复杂的动画,对其设置 position: fixed/absolute,尽可能地使元素脱离文档流,从而减少对其他元素的影响使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘避免使用 CSS 的 JavaScript 表达式
① 在使用 JavaScript 动态插入多个节点时, 可以使用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属性,比较糟糕,可以使用变量的形式缓存起来,待计算完毕再提交给浏览器发出重计算请求
// 缓存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,将其从页面上去掉,然后再进行后续 *** 作,这些后续 *** 作也不会触发回流与重绘,这个过程称为离线 *** 作。
const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
离线 *** 作后
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'
不过离线 *** 作在处理元素较少的情况下,DOM离线 *** 作优越性不太明显,因为离线 *** 作的原理是 将需要处理的元素 “ 拿下 ” 元素,处理完再放上去。这会触发一次性能损耗更大的回流! 但是在需要频繁 *** 作元素的情况下,相比多次触发重绘来说,只触发一两次回流,在全局相比较下,“ 拿下 ”的性能损耗就远远不足为虑了。
📃 参考文献 回流与重绘你真的了解回流和重绘吗 往期内容 💨
🔥 < 今日份知识点:Javascript本地存储的方式有哪些?区别及应用场景? >
🔥 < 每日份知识快餐:axios是什么?如何在Vue中 封装 axios ? >
🔥 < 在Vue中 el-popover + el-tiptap 实现 富文本框输入,表格点击展示 (富文本HTML标签渲染) >
🔥 < 知识拓展:CSS 中常用的计量单位有哪些? >
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)