美文网首页
Web性能优化-重绘与回流

Web性能优化-重绘与回流

作者: Ethan__Hunt | 来源:发表于2018-10-11 14:39 被阅读0次

    先看概念介绍:

    重绘 Paint

    当render tree中的一些元素需要更新属性,而这些属性只是影响元素的 外观风格,而不会影响布局的,比如background-color。则就叫称为重绘。

    咋看页面是不是在重绘? 重绘理解起来还不如重画,翻译成中文就是再画一遍.
    在Chrome的开发者工具中点 竖着的三个点 . 然后点More tools,然后选Rendering ,

    然后 重绘.png
    把这个checkbox 点上, 然后再看页面 中如果发生 重绘 的地方,就会用绿色闪过. 这个好观察 , 找一个timer 发现一秒变一次. 比如在淘宝上的这个 .
    image.png

    当然更直观一些的, 可以直接拖动滚动条. 因为很多资源没有加载出来或者 页面大小变更,触发重绘, 绿色的地方更明显.

    回流 Layout

    当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow).Layout
    页面布局几何属性 改变时就需要回流.
    页面布局:比如淘宝搜商品, 越往下滑,商品越多,这时候布局改变
    几何属性:比如一个图片点击之后变大.

    Note: 回流必将引起重绘,而重绘不一定会引起回流
    回流重点在 位置, 重绘重点在 效果.
    换了个位置, 需要在新的地方再画一个.

    触发页面重布局的属性

    • 盒子模型相关属性会触发重布局
    • 定位属性及浮动也会触发重布局
    • 改变节点内部文字结构也会触发重布局
    触发页面重布局的属性.png

    只触发重绘的属性


    只触发重绘的属性.png

    新建DOM的过程

    1. 获取DOM后分割为多个图层
    2. 对每个图层的节点计算样式结果(Recalculate style--样式重计算)
    3. 为每个节点生成图形和位置(Layout--回流和重布局)
    4. 将每个节点绘制填充到图层位图中(Paint Setup和Paint--重绘)
    5. 图层作为纹理上传至GPU
    6. 符合多个图层到页面上生成最终屏幕图像(Composite Layers--图层重组)

    通过Performance截取了一部分的淘宝页面中的轮播切换的位置 .就是一个图片切换到另外一个图片 ,浏览器做了些什么 ?


    Recalculate style.png Layout.png
    Update Layer Tree.png paint.png 最后Composite Layers.png

    GPU? 啥玩意是GPU? 我就听说过CPU , 我刚开始还以为是写错了.....
    GPU 简写来自 Graphic Processing Unit ,中文翻译为图形处理器. 简单点理解, 都知道CPU在电脑中的用处 , GPU在显卡中的作用 类似于CPU的作用. 专门的图形的核心处理器.

    图层? 什么叫图层 . 中文一层一层的图 ? 英文叫layers .
    还是Chrome 开发者工具, 点三个点, 然后More Tools ,然后选Layers.
    能看见页面中的图层, 我试了一下 发现, 百度的首页里边一个图层都没有.
    而淘宝的首页里边有N个图层 以至于 在点开每个图层的时候 非常卡.
    那么有个问题 图层咋来的 ?
    从Dom 元素变为 图层方式:

    1. CSS设置属性 transform:translateZ(0)
    2. CSS设置属性 will-change: transform

    频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素的重绘和回流的影响只会在这个图层

    比如video ,早期电影不就是一帧一帧的图片么....每次都会重绘.这个给他放在一个独立的图层中,效果更好. 看一下腾讯视频的video
    貌似行成图层的原因还挺多的 .
    比如这个 .


    image.png

    这是因为加了css 样式 ,就是上边说的第一个


    image.png

    那么对于video 是为啥就成了图层了 ?


    image.png

    意思也就是只要是个video 元素 他就是一个图层了.

    当然应该还有其他原因生成这些图层 就不举例子了 .还挺多的. 自己点点看吧

    Chrome创建图层的条件

    • 3D或透视变换(perspective transform)CSS属性
    • 使用加速视频解码的<video>节点
    • 拥有3D(WebGL)上下文或加速的2D上下文的<canvas>节点
    • 混合插件(如Flash)
    • 对自己的opacity做CSS动画或使用一个动画webkit变换的元素
    • 拥有加速CSS过滤器的元素
    • 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
    • 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)

    那么知道了之后怎么用?

    1. 避免使用触发重绘.回流的css属性
    2. 将重绘回流的影响范围限制在单独的图层之内

    实战优化点 这个更重要

    1. 用translate替代top改变 (优化测试: 详细例子在这里 )
    2. 用opacity替代visibility ,也就是用 opacity 0 或者1 来替代visibility
    3. 不要一条一条地修改 DOM 的样式,预先定义好 class,然后修改 DOM 的 className (常用)
    4. 把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 Reflow),然后你修改100次,然后再把它显示出来
    5. 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量 ,比如offsetHeight offsetWidth ,涉及到什么回流的缓存机制的. 记住吧.
    6. 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局.之前面试过Oracle的时候问过, 为啥不用table 布局了? a:对于搜索引擎的收录更加友好。b:w3c规范. c:占用空间小 d:改样式费劲 .e: 再加上回流这个.
    7. 动画实现的速度的选择.(别设置个1ms就动一次)
    8. 对于动画新建图层,
    9. 启用 GPU 硬件加速

    测试例子:

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            #rect{
                width:100px;
                height:100px;
                background-color: blue;
                position:absolute;
            }
        </style>
    </head>
    <body>
        <div id="rect"></div>
        <script>
            setTimeout(function(){
                document.getElementById("rect").style.top = '100px';
            },2000)
        </script>
    </body>
    </html>
    

    chrome中效果为


    image.png

    相比之下减少了Layout 也就是回流的过程

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            #rect{
                transform: translateY(0);
                width:100px;
                height:100px;
                background-color: blue;
            }
        </style>
    </head>
    <body>
        <div id="rect"></div>
        <script>
            setTimeout(function(){
                document.getElementById("rect").style.transform = 'translateY(100px)'
            },2000)
        </script>
    </body>
    </html>
    
    减少了Layout.png

    测一下visibility 和 opacity
    显示visibility ,会调用重绘.也就是Paint 就是第三个红柱子
    document.getElementById("rect").style.visibility = 'hidden';

    visibility.png

    然后是opacity,然后发现, 我靠,不是说opacity效率更高么怎么 不光重绘 还有回流了?
    document.getElementById("rect").style.opacity = '0';

    opacity.png

    关于这个 说是什么有阿尔法通道的还不是太理解. 先放着.
    然后把opacity里边的元素放到图层里边
    效果是这样

    image.png

    总体跟visibility差不多. 这一点先放着.暂时理解不是特别深.

    测试第三点

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            .rect{
                width:100px;
                height:100px;
                background-color: blue;
            }
            .action{
                top:100px;
                width:150px;
                height:150px;
                background-color: red;
                color:blue;
            }
        </style>
    </head>
    <body>
    <div class="rect"></div>
    <script>
        setTimeout(function(){
            document.getElementsByClassName("rect")[0].className = 'action'
        },2000)
    </script>
    </body>
    </html>
    

    效果如下 .只有一次.


    用className.png

    多次改变style的值

    效果是几乎差不多的.也没有多次的Layout , 猜测是chrome 内部已经可以处理这样的问题.


    image.png

    测试第四点: 相当于做一个离线 . 看效果

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            .rect{
                width:100px;
                height:100px;
                background-color: blue;
                display:none;
            }
        </style>
    </head>
    <body>
    <div class="rect"></div>
    <script>
        setTimeout(function(){
            console.log("change");
            document.getElementsByClassName("rect")[0].style.opacity = "0";
            document.getElementsByClassName("rect")[0].style.width = "150px";
            document.getElementsByClassName("rect")[0].style.height = "150px";
            document.getElementsByClassName("rect")[0].style.top = "100px";
            document.getElementsByClassName("rect")[0].style.backgroundColor = "red";
            document.getElementsByClassName("rect")[0].style.color = "blue";
            document.getElementsByClassName("rect")[0].style.width = "150px";
            document.getElementsByClassName("rect")[0].style.height = "150px";
            document.getElementsByClassName("rect")[0].style.top = "100px";
            document.getElementsByClassName("rect")[0].style.opacity = "1";
        },2000)
    </script>
    </body>
    </html>
    

    看下浏览器做了些啥 ?


    image.png

    接着是2秒之后让他显示出来


    image.png

    也就是说 如果你不显示出来, 浏览器就不会回流重绘.

    比较第五点的两种写法 :
    第一种写法:

        var doms = [];//通过选择器选择出一个dom元素的数组
        var domTop = [];//当前的可视区域的高度来计算这些doms元素的top值 也就是他们的位置
        for(var i = 0 ; i< doms.length; i++){
            var clientHeight = document.body.clientHeight;
            domTop.push(clientHeight + i * 100);
        }
    

    第二种写法

        var doms = [];//通过选择器选择出一个dom元素的数组
        var domTop = [];//当前的可视区域的高度来计算这些doms元素的top值 也就是他们的位置
        var clientHeight = document.body.clientHeight;
        for(var i = 0 ; i< doms.length; i++){
            domTop.push(clientHeight + i * 100);
        }
    

    第一种写法,在循环中会一直回去当前的可视区域的高度,因为每次都需要取最新的, 但是浏览器本身有一个缓冲区, 也就是说每次都需要冲完缓冲区,然后取一个最新的. 这样会破坏浏览器的缓冲机制 .

    相关文章

      网友评论

          本文标题:Web性能优化-重绘与回流

          本文链接:https://www.haomeiwen.com/subject/tkxbaftx.html