重绘(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

 

posted on 2018-12-07 10:47  薛小白  阅读(830)  评论(0编辑  收藏  举报