以下这段代码触发了几次回流?
ini
let el = document.getElementById('app');
el.style.width = (el.offsetWidth + 1) + 'px';
el.style.width = 1 + 'px';
// 这段代码为什么在chorme浏览器的 performance 中只触发了一次 Layout 事件,你猜对了嘛?
首先我们得搞明白什么是回流,顺便一起了解下重绘?
回流: 也叫重排(reflow)发生在浏览器重新计算网页的某些部分的位置和几何形状时(例如在交互式站点更新后)这通常会紧更新这重绘即浏览器重新绘制网页以显示更新后的视觉效果。
重绘: 是在浏览器重新绘制网页以显示由 UI 更改引起的视觉更新时发生,例如在交互式站点上进行更新后,这通常是在重排之后发生的,重排是浏览器重新计算网页的某些部分的位置和几何形状。
嗯!!!我明白了然后呢???知道这个定义也没办法得出结论啊???
那我们在了解一下浏览器渲染的过程。
- 浏览器开始加载网页的HTML文档;
- 浏览器开始解析HTML文档,构建DOM(文档对象模型)树
- 同时或随后,浏览器解析Css 文件(可能包括外部css 文件、内嵌样式和行样式),构建cssom(层叠样式表对象模型)树;
- 将DOM树与CSSOM树合并成Render树这个树包含了要渲染的可见元素以及他们的样式信息;
- 浏览器进行布局计算确定每个元素的大小和位置。这个过程称为回流(Reflow),他看可能会因为DOM结构的变化、元素的尺寸的变化、或者浏览器窗口大小的变化触发。
- 根据布局计算的结构,浏览器绘制元素的外观,包括颜色、边框、背景等这个过程称为重绘(Repaint),它可能会因为元素样式的变化(如颜色变化)而触发,而不一定涉及布局的变化。
- 为了提高渲染性能,浏览器可能会将网页分成多个图层,并在这些图层上进行合层渲染。这过程称为合成(Compositing)
- 最后,浏览器会将渲染好的网页显示在屏幕上;
现在我们知道了 回流是计算页面的每个可见节点的位置以及大小;重绘是在浏览器上重新绘制render 树;
根据文档介绍我们知道回流会影响整个渲染树,计算复杂度高需要递归遍历所有节点。回流后还会触发后续渲染阶段重绘和合成;在许多情况下,它们相当于重新布局整个页面。开销非常大影响浏览器的渲染性能;
那么影响浏览器回流的操作有哪些?
操作类型 | 示例 |
---|---|
几何属性修改 | width, height, padding, margin |
布局相关属性 | display, position, float |
内容变化 | 文本增减、图片加载 |
窗口尺寸变化 | resize 事件 |
获取布局属性 | offsetTop, scrollHeight, getComputedStyle |
这会我知道啦!3次!!!
ini
//再看一下题目
let el = document.getElementById('app');
el.style.width = (el.offsetWidth + 1) + 'px';
el.style.width = 1 + 'px';
是的,看到这儿我们可能很容易就得出答案 此代码回流触发了3次
1、获取 el.offsetWidth 回流一次;
2、第一次设置 width 回流一次;
3、第二次设置 width 回流一次;
一共3次!!!;
如何验证?
那么我们该如何验证得出的答案是否正确呢?
好在 Chorme 浏览器中有 Performance这个神器,可以看到只会触发一次 Layout 事件,而Layout 即回流触发的事件;
啊!!!怎么会只有一次呢?
原来现代的浏览器自带优化机制,在短时间内多次对DOM 或样式的修改操作缓存到一个队列中,等待合适的时机(如脚本执行完毕、任务空闲时)批量处理,近触发一次回流。而遇到立即读取布局属性(如 offsetWidth 、clientWidth、scrollWidth)会迫使浏览器清空队列并立即回流。
这样就说的通了,为什么在preformance 中只有一条 Layout 日志;
- 获取 el.offsetWidth 因为浏览器清空队列本来就是空的所以不会产生回流;
- 第一次设置 width ;这个操作会被浏览器推进渲染队列,并不会立即执行回流;
- 第二次设置 width ;这个操作也会被浏览器推进渲染队列,但是后面也没有操作了所以在下一帧回流一次完成渲染;
最后我们再来看一下哪些操作可以减少回流?提升渲染性能
- 使用 CSS 动画代替 javaScript动画,CSS 动画利用GPU加速,在性能方面通常比JavaScript 动画更高; 使用translate3d 同样可以开启 GPU 加速;
- 避免频繁操作影响布局的样式属性,当需要对元素进行多次样式修改时,可以考虑修改合并一次操作。通过添加或移除 css 类一次性修改而不是逐个修改;
- 使用requestAnimationFrame方法调度动画帧,可以确保动画在浏览器的重绘周期内执行,从而避免不必要的回流。这种方式可确保在最佳时间点进行渲染;
- 使用 Document Fragment 通过这个API可以先将元素添加到文档片段中,然后再将整个文档片段一次性插入DOM。减少回流次数,有点像虚拟DOM;
- 让元素脱离文档流,absolute/fixed/left;