重绘和重排都是发生在浏览器呈现引擎(渲染引擎)中的由于DOM元素改变所作出的事件过程。重绘不一定引起重排,而重排一定引起重绘。
重绘:(Repaint) 有的资料也被叫做改型( Restyling),是由于元素的外观样式属性的改变所触发的行为,如visibility、背景颜色,边框颜色等属性。
重排:(Reflow)也被成为重新布局(Relayout),是由于元素的结构属性(或者说是几何属性)改变所触发的行为,主要场景如下:
通常浏览器对重绘和重排做了一些优化处理,比如:
另外比较有趣的是,
如下只也会引起一次重绘和一次重排:
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;\
主动调用重排,也就是第二方案就是我们要的效果。
浏览器的工作原理:新式网络浏览器幕后揭秘
Rendering: repaint, reflow/relayout, restyle