《浏览器基础》之重绘与重排

概览

重绘和重排都是发生在浏览器呈现引擎(渲染引擎)中的由于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

转载于:https://www.cnblogs.com/lvyongbo/p/5925833.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重绘重排是CSS渲染过程中的两个重要概念。 重绘(Repaint)指的是当元素的样式发生变化,但不影响其布局时,浏览器将新样式应用到元素上,重新绘制元素的外观。重绘的开销相对较小,不引起布局的变化。 而重排(Reflow)指的是当页面布局发生变化时,例如修改了元素的尺寸、位置、内容等,浏览器重新计算并更新元素的几何属性(如大小、位置),然后重新布局页面。重排的开销相对较大,因为它涉及到整个页面或部分页面的重新渲染。 重绘重排的区别在于是否引起布局的变化。重绘重新绘制元素的外观,而不影响其周围元素的布局;而重排导致整个渲染树的重新构建和布局。 在性能优化方面,我们通常要尽量减少重排重绘的次数,因为它们消耗大量的计算资源。一些常见的优化方法包括: 1. 使用 CSS3 动画或过渡代替 JavaScript 实现的动画效果,因为后者可能导致频繁的重排重绘; 2. 使用类似 flexbox 和 grid 等布局技术,可以减少页面布局的复杂性,降低重排重绘的次数; 3. 避免频繁访问引起重排重绘属性,例如 offsetTop、offsetLeft、scrollTop、clientWidth 等; 4. 批量更新样式或布局,可以使用 CSS 类名的方式一次性修改多个元素的样式,而不是逐个修改; 5. 将需要执行多次重排的 DOM 操作尽量合并为一次,使用文档片段(DocumentFragment)进行缓存。 通过合理优化和减少重排重绘的次数,可以提升页面的性能和响应速度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值