重排(回流)和重绘 | 青训营

重排必定会引发重绘,但重绘不一定会引发重排 重排(Reflow) && 重绘(Redraw)会付出高昂的性能代价

重绘

指的是当元素的样式(如颜色、背景、边框等)发生改变,但不影响其在文档流中的位置和大小时,浏览器会重新绘制(重新渲染)该元素,以更新其显示样式。重绘不会影响其他元素的布局和渲染,例如:修改元素的填充颜色,会触发重绘。

下面样式的修改会发生重绘:

  • color
  • border-style
  • border-radius
  • text-decoration
  • box-shadow
  • outline
  • background

重排

指的是当元素的布局属性发生改变,例如改变元素的尺寸、位置、显示/隐藏等,会导致浏览器重新计算并应用所有受影响元素的几何属性(包括位置、尺寸等),然后进行重新布局。重新生成布局,重新排列元素(重新计算各节点和css具体的大小和位置:渲染树需要重新计算所有受影响的节点 例如:改元素的宽高,会触发重排

下面样式的修改会发生重绘:

  • 页面初始渲染,这是开销最大的一次重排
  • 添加/删除可见的DOM元素
  • 改变元素位置
  • 改变元素尺寸,比如边距、填充、边框、宽度和高度等
  • 改变元素内容,比如文字数量,图片大小等
  • 改变元素字体大小
  • 改变浏览器窗口尺寸,比如resize事件发生时
  • 激活CSS伪类(例如::hover)
  • 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow
  • 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等
  • 当我们调用getComputedStyl方法,或者IE里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个"即时性"和"准确性"

从范围来看

  • 全局范围(全局布局) :从根节点html开始对整个渲染树进行重新布局;
  • 局部范围(局部布局) :对渲染树的某部分或某一个渲染对象进行重新布局;
  • 不难看出,我们应该尽量减少重排影响的范围

优化思路

样式集中改变(减少重排次数

当你试图对一个dom进行多次的样式修改的时候,不妨试着合并操作

上述操作有可能导致导致 3次重绘和1次重排,更常见的做法是通过动态的添加class,在一次操作里实现效果,从而减少重绘的可能

试着把DOM离线

当你试图对一个对象进行多次多种的操作,比如color、background、padding,marginLeft、marginTop的改动,不但是多次的还是多种多样的 我建议你可以把dom先进行离线操作,把它从渲染树上移走,直到你操作完毕,对隐藏的DOM元素操作不会引发其他元素的重排,这样只在隐藏和显示时触发2次重排

脱离文档流: 使用 absolute 或 fixed 脱离文档流

你可以试着将需要重排的元素,position属性设为absolute或fixed(某些特殊场合),减小重排范围

善用内存:在内存中多次操作DOM,再整个添加到DOM树(减小重排范围)

在我最近做的一个demo里,我试着给我的切片上传增加动画,我想把每个切片的上传情况和编码显示在页面上

思路很自然,往预设的ul元素里不断丢li进去,这样100个盒子就放进去了,但是如果你是分100次丢进去,每次添加都可能触发重排,因为每个子元素的添加都会引起父元素的布局计算,当然这边不考虑现代浏览器的批量重排的优化方式,我们很难指望别人的优化手段,只能尽力的避免浪费

我采取的是ul也不先放容器里,而是用li把它喂饱之后再一次性把ul放到容器里

读写分离:将写入的值缓存,读取缓存的值

前面提到有一些浏览器针对重排会做出优化,当你触发重排的条件到达一定量的时候, 或者等到一定时间的时候,或者等一个线程结束,再一起推迟到浏览器下一次绘制的时候进行重排,但是当获取一些属性时,浏览器为取得正确的值也会触发重排,浏览器的优化在这里就无可奈何了

这些属性包括

  • offsetWidth offsetHeight offsetTop ofsetLeft
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle() (currentStyle in IE)。

如果你在代码里多次获取这些值,一次有多处用到,尽量避免每次用的时候都通过.style.offsetWidth获取,你应该试着在一次运行过程里通过缓存避免多次调用

相关推荐
千慌百风定乾坤4 小时前
Go 语言入门指南:基础语法和常用特性解析(下) | 豆包MarsCode AI刷题
青训营笔记
FOFO4 小时前
青训营笔记 | HTML语义化的案例分析: 粗略地手绘分析juejin.cn首页 | 豆包MarsCode AI 刷题
青训营笔记
滑滑滑2 天前
后端实践-优化一个已有的 Go 程序提高其性能 | 豆包MarsCode AI刷题
青训营笔记
柠檬柠檬2 天前
Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题
青训营笔记
用户967136399652 天前
计算最小步长丨豆包MarsCodeAI刷题
青训营笔记
用户52975799354723 天前
字节跳动青训营刷题笔记2| 豆包MarsCode AI刷题
青训营笔记
clearcold3 天前
浅谈对LangChain中Model I/O的见解 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵4 天前
【字节青训营】 Go 进阶语言:并发概述、Goroutine、Channel、协程池 | 豆包MarsCode AI刷题
青训营笔记
用户336901104444 天前
数字分组求和题解 | 豆包MarsCode AI刷题
青训营笔记
dnxb1234 天前
GO语言工程实践课后作业:实现思路、代码以及路径记录 | 豆包MarsCode AI刷题
青训营笔记