前端之旅前端首页投稿(暂停使用,暂停投稿)

关于网页渲染,前端们应该知道的一些事情(译)

2016-06-02  本文已影响461人  7091a52ac9e5

浏览器是如何渲染网页的?

我们从浏览器渲染一个页面的行为说起:

当用户与一个页面交互或者脚本修改时,由于文档结构的变化,以上的一些操作步骤会重新执行。

重现绘制(Repaint)

当元素样式变化并不影响该元素在一个网页上的位置时(background-colorborder-color,visibility),浏览器只会把新样式应用到该元素。

Reflow

当改变影响了文档内容或结构或者元素的位置时,reflow发生(重新布局),这一般由以下因素触发:

怎么让浏览器充分利用渲染机制

浏览器会尽可能的限制被改变元素所在区域的重排重绘,比如说display:fixed/absolute元素改变时只会影响它本身和它的子元素,而display:static元素改变时会使其随后的元素都被重绘;(影响尽量少的元素)
另一个最大化性能的机制在于,当运行一系列JavaScript片段时,浏览器会缓存它们,然后一次运行。看下面的例子可以很好的理解:

var $body = $('body');
$body.css('padding', '1px'); // reflow, repaint
$body.css('color', 'red'); // repaint
$body.css('margin', '2px'); // reflow, repaint
// only 1 reflow and repaint will actually happen(由于缓存,只会重绘一次)

然而,就像上面已经提到的,调用一个元素的属性会触发强制性的reflow,当我们加上一行读元素属性的代码时就会发生;

var $body = $('body');
$body.css('padding', '1px');
$body.css('padding'); // reading a property, a forced reflow(强制发生)
$body.css('color', 'red');
$body.css('margin', '2px');
//另外一次reflow

因此,会有两次reflow,因此应该组合来读元素属性已最大化性能一个详细的例子

$(function() {
  var $body = $('body');

  $body
  .on('click', '.block-1', function(e) {
    // 1 reflow
    
    $body.css('padding', '1px');
    $body.css('color', 'red');
    $body.css('margin', '2px');
  })

  .on('click', '.block-2', function(e) {
    // 2 reflows
    
    $body.css('padding', '1px');
    $body.css('padding');
    $body.css('color', 'red');
    $body.css('margin', '2px');
  })

  .on('click', '.block-3', function(e) {
    // 3 repaints
    
    $body.css('color', 'red');
    $body.css('color');
    $body.css('color', 'yellow');
    $body.css('background');
    $body.css('color', 'blue');
    $body.css('outline');
  })

  .on('click', '.block-4', function(e) {
    // 1 repaint
    
    $body.css('color', 'red');
    $body.css('color', 'yellow');
    $body.css('color', 'blue');

    $body.css('color');
    $body.css('background');
    $body.css('outline');
  })

  .on('click', '.block-5', function(e) {
    // 3 reflows
    
    $body.css('padding', '1px');
    $body[0].offsetHeight;
    $body.css('padding', '2px');
    $body[0].offsetTop;
    $body.css('padding', '3px');
    $body[0].offsetWidth;
  })

  .on('click', '.block-6', function(e) {
    // 1 reflow
    
    $body.css('padding', '1px');
    $body.css('padding', '2px');
    $body.css('padding', '3px');

    $body[0].offsetHeight;
    $body[0].offsetTop;
    $body[0].offsetWidth;
  });
});

有些时候,可能你会需要一次强制性的reflow,例如:我们需要运用两次margin-left到同一个对象,第一次无动画的设置到100px,然后通过动画过渡到50px,实例
过渡动画:

.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;
}

过程代码:

// our element that has a "has-transition" class by default
var $targetElem = $('#targetElemId');

// remove the transition class
$targetElem.removeClass('has-transition');

// change the property expecting the transition to be off, as the class is not there
// anymore
$targetElem.css('margin-left', 100);

// put the transition class back
$targetElem.addClass('has-transition');

// change the property
$targetElem.css('margin-left', 50);

上述代码并不按预期工作,因为改变被缓存并在最后执行了一次,这时候我们就需要一次强制性的执行了:

达到预期效果的代码

// remove the transition class
$(this).removeClass('has-transition');

// change the property
$(this).css('margin-left', 100);

// trigger a forced reflow, so that changes in a class/property get applied immediately
$(this)[0].offsetHeight; // an example, other properties would work, too

// put the transition class back
$(this).addClass('has-transition');

// change the property
$(this).css('margin-left', 50);

现在达到预期效果了!

性能优化的建议

总结了一些有用的信息,本文有以下建议

div * {...} // bad
.list li {...} // bad
.list-item {...} // good
#list .list-item {...} // good

延展阅读已获得更多信息:

  1. How browsers works;
  2. Rendering: repaint, reflow/relayout, restyle;

希望这边译文对您有用

原文链接

上一篇 下一篇

猜你喜欢

热点阅读