zoukankan      html  css  js  c++  java
  • 《浏览器基础》之重绘与重排

    概览

    重绘和重排都是发生在浏览器呈现引擎(渲染引擎)中的由于DOM元素改变所作出的事件过程。重绘不一定引起重排,而重排一定引起重绘。
    重绘:(Repaint) 有的资料也被叫做改型( Restyling),是由于元素的外观样式属性的改变所触发的行为,如visibility、背景颜色,边框颜色等属性。
    重排:(Reflow)也被成为重新布局(Relayout),是由于元素的结构属性(或者说是几何属性)改变所触发的行为,主要场景如下:

    • DOM树节点的操作比, 如:Resizing, Removing, Adding 。
    • 文本的改变,如:text。
    • 浏览器窗口的改变,如: Resizing, Scrolling。
    • 伪类的激活,如::hover。
    • Class 属性改变。
    • Css 属性改变。
    • 新的stylesheets 被添加或者旧的被删除。

    浏览器对重排的优化:

    通常浏览器对重绘和重排做了一些优化处理,比如:

    • 如果改变一个absolute或者 fixed定位的元素,那么只会重排这个元素和他的子元素,但是当改变了static定位的元素,那么整个页面都会重排。

    • 另外比较有趣的是,
      如下只也会引起一次重绘和一次重排:

      var $body = $('body');
      $body.css('padding', '1px'); //no reflow, repaint
      $body.css('color', 'red'); //no repaint
      $body.css('margin', '2px'); // reflow, repaint

    如上描述,并不是每次属性改变都会引起重排或重绘,有时,它会等一段代码执行结束再一起处理,这样就只发生一次重排。但是如果直接获取属性值,浏览器会强制发生重排来确保获取真实的值。如下:

    var $body = $('body'); 
    $body.css('padding', '1px'); 
    $body.css('padding'); // forced reflow 
    $body.css('color', 'red'); 
    $body.css('margin', '2px');
    

    总共我们得到了两此重排,由此看出浏览器的优化失败了。So 如果你想更改一些属性并且想使他们获得如你所愿的性能的时候,可以一起执行修改,然后再获取属性。具体参照下如下的代码:

    <!DOCTYPE html>
    <html>
    <head>
    <script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <style type="text/css">
    .block {
      padding: 50px;
      margin: 10px;
      background: #ccc;
    }
    </style>
    <script type="text/javascript">
    $(function() {
        var $body = $('body');
    
        // 1 reflow
        $body.on('click', '.block-1', function(e) {
            $body.css('padding', '1px');
            $body.css('color', 'red');
            $body.css('margin', '2px');
        })
    
        // 2 reflows
        .on('click', '.block-2', function(e) {
            $body.css('padding', '1px');
            $body.css('padding');
            $body.css('color', 'red');
            $body.css('margin', '2px');
        })
    
        // 3 repaints
        .on('click', '.block-3', function(e) {
            $body.css('color', 'red');
            $body.css('color');
            $body.css('color', 'yellow');
            $body.css('background');
            $body.css('color', 'blue');
            $body.css('outline');
        })
    
        // 1 repaint
        .on('click', '.block-4', function(e) {
            $body.css('color', 'red');
            $body.css('color', 'yellow');
            $body.css('color', 'blue');
    
            $body.css('color');
            $body.css('background');
            $body.css('outline');
        })
    
        // 3 reflows
        .on('click', '.block-5', function(e) {  
            $body.css('padding', '1px');
            $body[0].offsetHeight;
            $body.css('padding', '2px');
            $body[0].offsetTop;
            $body.css('padding', '3px');
            $body[0].offsetWidth;
        })
    
        // 1 reflow
        .on('click', '.block-6', function(e) {
            $body.css('padding', '1px');
            $body.css('padding', '2px');
            $body.css('padding', '3px');
    
            $body[0].offsetHeight;
            $body[0].offsetTop;
            $body[0].offsetWidth;
        });
    });
    </script>
    </head>
    <body>
        <div class="block block-1">Click to run example #1 (1 reflow)</div>
        <div class="block block-2">Click to run example #2 (2 reflow)</div>
        <div class="block block-3">Click to run example #3 (3 repaint)</div>
        <div class="block block-4">Click to run example #4 (1 repaint)</div>
        <div class="block block-5">Click to run example #5 (3 reflow)</div>
        <div class="block block-6">Click to run example #6 (1 reflow)</div>
    </body>
    </html>
    

    有时我们不能避免重排的发生。比如,我们需要设置两次margin-left。第一次设置它100px(没有动画),第二次设置为50px(有动画)。代码如下:

    <!DOCTYPE html>
    <html>
    <head>
    <title></title>
    <script type="text/javascript">
        $('.example-1 li').click(function(){
        $(this).removeClass('has-transition');
        $(this).css('margin-left', 100);
        $(this).addClass('has-transition');
        $(this).css('margin-left', 50);
    });
    $('.example-2 li').click(function(){
        $(this).removeClass('has-transition');
        $(this).css('margin-left', 100);
        $(this)[0].offsetHeight; // 强制执行重排,确保设置的100px能够生效
        $(this).addClass('has-transition');
        $(this).css('margin-left', 50);
    });
    </script>
    <style type="text/css">
    .has-transition {
        -webkit-transition: margin-left 1s ease-out;
        -moz-transition: margin-left 1s ease-out;
        -o-transition: margin-left 1s ease-out;
        transition: margin-left 1s ease-out;
    }
    li {
        background: #ccc;
        border: 1px #000 solid;
        display: block;
        padding: 2px;
        margin-left: 0;
        margin-top: 4px;
        margin-bottom: 4px;
    }
    </style>
    </head>
    <body>
    <p>第一种情况)</p>
    <ul class="example-1">
        <li class="has-transition">1</li>
        <li class="has-transition">2</li>
        <li class="has-transition">3</li>
        <li class="has-transition">4</li>
        <li class="has-transition">5</li>
    </ul>
    <p>第二种情况)</p>
    <ul class="example-2">
        <li class="has-transition">1</li>
        <li class="has-transition">2</li>
        <li class="has-transition">3</li>
        <li class="has-transition">4</li>
        <li class="has-transition">5</li>
    </ul>
    </body>
    </html>
    

    如上,因为浏览器的缓存的原因只会在脚本的末尾才重排的特性,第一种方案是无法满足需求的,而我们需要有一个重排所以加上一个获取属性的代码$(this)[0].offsetHeight; 主动调用重排,也就是第二方案就是我们要的效果。

    最后是我最近工作中总结的一些关于前端优化的小技巧:

    • 在<head>标签内引用样式,在<body>标签后面引用scripts
    • 尽量让css选择器简单,直观(即使你使用的是预处理程序),越少嵌套越好。选择器的效率排名如下(第一个是最快的):
      1. Identificator: #id
      2. Class: .class
      3. Tag: div
      4. Neighbour selector: a + i
      5. Children selector: ul > li
      6. Universal selector: * . Attribute selector: inputtype=”text”
      7. Pseudoelements and pseudoclasses: a:hover
    • 尽量减少对DOM的操作,如果你对某个属性和对象要进行多次操作,要缓存它们。如果要完成复杂操作的时候,可以多用离线存储技术。
    • 最好只用.class设置元素的样式。
    • 所有的动画都设置为fixed或者absolute定位(要不就不要用动画)。
    • 重排是一个非常昂贵的操作,尽可能多的减少重排的次数和重排的影响范围。
    • 当页面滚动的时候禁用所有的:hover。

    参考文章

    浏览器的工作原理:新式网络浏览器幕后揭秘
    Rendering: repaint, reflow/relayout, restyle

  • 相关阅读:
    Sikulix 多个相似图片的选择
    Sikulix选取相对位置的图片或对象
    Sikulix 实用方法
    两个Excel内容比较
    SIkulix在Eclipse中的使用
    Sikulix IDE简介
    安装Sikulix
    Sikuli简介
    建立连接ALM的xml config文件
    XML序列化成对象
  • 原文地址:https://www.cnblogs.com/lvyongbo/p/5925833.html
Copyright © 2011-2022 走看看