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 > 改变 Windows 大小
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
来源: https://www.cnblogs.com/yangyangxxb/p/10081546.html