Web性能优化-重绘与回流

先看概念介绍:

重绘 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);
    }

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容