重绘(Repaint)和回流(Reflow)
1、回流和重绘只是渲染步骤的一小节,是怎么做到影响性能的?
css 会影响 javascrip 执行时间导致 javascript 脚本变慢
浏览器渲染一个网页的时候会启用两条线程:
一条渲染javascript 脚本,另一条渲染 ui 即css 样式的渲染。
但这两条线程是互斥的。当javascript 线程运行的时候 ui 线程则会中止暂停,反之亦然。
那这是为什么呢?
原因是,当ui 线程运行对页面进行渲染的时候 js 脚本难免会涉及到页面视图上的一些样式的改变,
为了使这个改变更加准确 js 脚本只好等待ui 线程渲染完成的时候才去执行。
所以当一个页面的元素样式改动频繁的时候ui 线程就会持续渲染,造成js 代码反应慢半拍,卡顿的情况。
2、什么是回流?什么是重绘?
a、回流:布局或者几何属性需要改变就称为回流。
当render tree 的一部分或全部的元素因改变了自身的宽高,布局,显示或隐藏,
或者元素内部的文字结构发生变化 导致需要重新构建页面的时候,回流就产生了
b、重绘:重绘是当节点需要更改外观而不会影响布局的,比如改变 color
就叫称为重绘。
当一个元素自身的宽高,布局,及显示或隐藏没有改变,而只是改变了元素的外观风格的时候,就会产生重绘。
例如你改变了元素的background-color
c、因此,回流必定会发生重绘,重绘不一定会引发回流。
回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流。
所以以下几个动作可能会导致性能问题:
1>改变 window 大小
2>改变字体
3>添加或删除样式
4>文字改变
5>定位或者浮动
6>盒模型
3、什么会引起回流?
大概分为五类:
a、首当其冲自然是dom树结构变化,比如你删除或者添加某个node.
b、元素几何属性变化,包括margin,padding,height,width,border等
c、页面渲染初始化
d、获取某些属性(offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE))
e、浏览器窗口发生变化-resize事件发生时
var s = document.body.style; s.padding = "2px"; // 回流+重绘 s.border = "1px solid red"; // 再一次 回流+重绘 s.color = "blue"; // 再一次重绘 s.backgroundColor = "#ccc"; // 再一次 重绘 s.fontSize = "14px"; // 再一次 回流+重绘 // 添加node,再一次 回流+重绘 document.body.appendChild(document.createTextNode('abc!'));
4、什么会引起重绘?
5、如何避免?
a、使用 translate
替代 top
举个🌰
这是使用top
<div class="test"></div> <style> .test { position: absolute; top: 10px; width: 100px; height: 100px; background: red; } </style> <script> setTimeout(() => { // 引起回流 document.querySelector('.test').style.top = '100px' }, 1000) </script>
这是使用translate
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>CSS3位移translate()方法</title> <style type="text/css"> /*设置原始元素样式*/ #origin { margin:100px auto;/*水平居中*/ width:200px; height:100px; border:1px dashed silver; } /*设置当前元素样式*/ #current { width:200px; height:100px; color:white; background-color: #3EDFF4; text-align:center; transform:translateX(50px); -webkit-transform:translateX(20px); /*兼容-webkit-引擎浏览器*/ -moz-transform:translateX(20px); /*兼容-moz-引擎浏览器*/ } </style> </head> <body> <div id="origin"> <div id="current"></div> </div> </body> </html> //总结:transform:translateX(50px) //说明在水平方向上移动50px, //这时候我们如果我们把50px改成-50px, //该元素就在水平方向相反的方向移动50px。 //translate(y)的用法: //translate(y)的用法和translate(x)的用法很相似, //x是在水平方向上移动, //y是在垂直方向上移动, //当y出现正值的时候,说明该元素在向下移动, //如果出现负值,说明是向上移动,和我们的正常思维相反。 //translate(x,y)的混合使用: //表示元素在x中水平方向上移动,这里需要注意的是, //y值是一个参数,没有设置y的值话,可能表示元素在x轴移动, //其实单纯的使用translate()是没有太多的意义,我们可以结合css使用。
b、使用 visibility
替换 display: none
,因为前者只会引起重绘,后者会引发回流(改变了布局)
visibility 属性规定元素是否可见。
提示:即使不可见的元素也会占据页面上的空间
display 属性规定元素是否可见。
提示:不可见的元素不占据页面上空间
c、 把 DOM 离线后修改,比如:先把 DOM 给 display:none
(有一次 Reflow),然后你修改100次,然后再把它显示出来
d、不要使用 js 代码对dom 元素设置多条样式,选择用一个 className 代替
e、不要把 DOM 结点的属性值放在一个循环里当成循环里的变量
for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }
f、不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局,
table 的每一个行甚至每一个单元格的样式更新都会导致整个table 重新布局
g、动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
//requestAnimationFrame是什么呢? //他也是个计时器 //与setTimeout和setInterval不同, //requestAnimationFrame不需要设置时间间隔 //setTimeout和setInterval的问题是,它们都不精确。 //它们的内在运行机制决定了时间间隔参数实际上只是 //指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。 //如果队列前面已经加入了其他任务, //那动画代码就要等前面的任务完成后再执行 //requestAnimationFrame采用系统时间间隔, //保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销; //也不会因为间隔时间太长,使用动画卡顿不流畅, //让各种网页动画效果能够有一个统一的刷新机制, //从而节省系统资源,提高系统性能,改善视觉效果 //【1】requestAnimationFrame会把每一帧中的所有DOM操作集中起来, // 在一次重绘或回流中就完成, // 并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率 //【2】在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流, // 这当然就意味着更少的CPU、GPU和内存使用量 //【3】requestAnimationFrame是由浏览器专门为动画提供的API, // 在运行时浏览器会自动优化方法的调用, // 并且如果页面不是激活状态下的话,动画会自动暂停, //有效节省了CPU开销 //控制台输出1和0 var timer = requestAnimationFrame(function(){ console.log(0); }); console.log(timer);//1 //控制台什么都不输出 var timer = requestAnimationFrame(function(){ console.log(0); }); cancelAnimationFrame(timer); //cancelAnimationFrame方法用于取消定时器
h、将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video
标签,浏览器会自动将该节点变为图层。
i、CSS 选择符从右往左匹配查找,避免 DOM 深度过深
.mod-nav h3 span {font-size: 16px;} //如果不知道匹配规则,可能的理解是从左向右匹配:先找到.mod-nav, //然后逐级匹配h3、span, //在这个过程中如果遍历到叶子节点都没有匹配就需要回溯, //继续寻找下一个分支。 //但事实上,CSS选择器的读取顺序是从右向左。 //还是上面的选择器,它的读取顺序变成:先找到所有的span, //沿着span的父元素查找h3, //中途找到了符合匹配规则的节点就加入结果集; //如果直到根元素html都没有匹配,则不再遍历这条路径, //从下一个span开始重复这个过程(如果有多个最右节点为span的话)。
以上。
参考: 文献1:http://www.php.cn/css-tutorial-412430.html 文献2:http://www.w3school.com.cn/cssref/pr_class_visibility.asp
文献3:http://www.cnblogs.com/xiaohuochai/p/5777186.html
文献4:https://www.cnblogs.com/zhaodongyu/p/3341080.html