本人已参与「新人创作礼」活动,一起开启掘金创作之路。
什么是重绘和回流?
对于dom上的性能优化追根揭底是对重绘和节流的把控。
重绘:DOM节点元素没有删除,增加,移动,尺寸改变的情况,元素只有样式上的改变。浏览器针对这个样式改变对视图进行一个重新绘制。这个过程,叫做重绘。比如color,background的改变。就是典型的重绘。
回流:回流也叫做重排,指DOM节点元素出现删除,增加,移动,尺寸改变的情况。浏览器需要针对元素进行重新构建和渲染。这个过程被称为回流。
浏览器在接收到一个html文档的时候会做些什么?↓
浏览器在接收到一个html文档的时候,会同时构建DOM节点树和CSS渲染树。注意,出于对浏览器性能上的考虑,浏览器并不会等待DOM节点树构建完毕后再去构建CSS渲染树,所以DOM节点树的构建和CSS渲染树的构建是同步的。
浏览器绘制流程如下👇
在这个过程中,如果在DOM节点树中扫描到JS代码,DOM节点树和CSS渲染树会停止构建,浏览器把控制权交给javascript的引擎。并且等待这一段JS代码运行完毕后再继续构建(因为浏览器不知道js代码是否会对后续的节点进行操作,万一那段JS代码是把dom即将构建的节点删掉咋办,那就白建了。所以需要等待混在dom中的js代码执行完毕之后再进行构建)。
重绘不一定会导致回流。但是回流一定需要重绘。而且,回流的性能消耗远大于重绘。
为了让重绘和回流的影响对我们的产品影响降低到最小,vue,react,angular等框架都使用了虚拟dom技术,就是避免浏览器进行不必要的重绘和回流。
重绘和回流的触发场景
重绘的触发场景↓
- 颜色的修改(color,background-color)
- 阴影的修改
- visibility:hidden
- css3的translate
- ...
回流的触发场景↓
- 删除或者新增一个节点元素
- 元素位置的改变,比如float,position,overflow,display等等
- 元素尺寸的改变,比如margin,padding,height,width等等
- 初始化构建DOM树的时候
- 窗口尺寸的变化 也就是resize事件发生的时候
- 填充内容的改变(内容撑大了某一个节点,内容改变,包含它的节点大小自然跟随调整。)
- 读取某一个元素的时候,比如offsetLeft,offsetTop,offsetHeight,offsetWidth, clientTop,clientLeft,clientWidth,clientHeight, scrollTop,scrollLeft,scrollWidth,scrollHeight, width,height等等
- ...
减少重绘和回流
-
当我们需要动态改变某一个元素的位置的时候,不要使用position,使用translate,两种功能都可以实现元素的位移,但是position会触发回流,但是translate只会触发重绘。
-
尽量不要使用table布局,因为table中某一个小小的改动就会导致整个table进行重新布局。而且table的重新构建往往是其他元素的几倍。
-
避免频繁获取节点或者某一个会导致回流的元素。(例如 element.offsetLeft,document.getElementById)仅仅是读取这个元素就会导致回流,所以,条件允许的话,读取一次就缓存起来。避免多次读取。
-
可以使用css预先构建好的样式不要使用js去动态添加。
-
再translate无法满足元素移动需求的情况下,让元的position变成absolute或者fixed也就是脱离文档流之后再去移动,否则像relative此类状态去移动的话会导致后续全部元素陷入高频的回流状态。
-
需要隐藏元素的时候使用visibility:hidden而非display:none.display:none会导致回流但是visibility:hidden不会
-
离线操作:对dom进行操作的时候,可以先使用diaply:none将元素离线,操作完成以后再display:block显示。
-
....
总结
- 重绘:尺寸没变,大小没变,也没有突然出现,改变的仅仅是颜色,轮廓等。
- 回流: 尺寸变了,大小变了,或者突然新增了一个节点。
- 重绘不一定会引发回流,但是回流一定会引发重绘。
- 开发中需要尽量避免重绘和回流。
欢迎技术沟通,摸鱼聊天~
备注来自掘金~
wx:XXF1096032096