前端技术Web前端之路让前端飞

网页性能管理:重绘和重排

2017-08-18  本文已影响108人  葶寳寳

网页生成的过程

要理解网页性能为什么不好,就要了解网页是怎么生成的。

网页的生成过程,大致可以分成五步:
1.HTML代码转化成DOM
2.CSS代码转化成CSSOMCSS Object Model)。
3.结合DOMCSSOM,生成一棵渲染树(包含每个节点的视觉信息)。
4.生成布局(layout),即将所有渲染树的所有节点进行平面合成。
5.将布局绘制(paint)在屏幕上。

"生成布局"(flow)和"绘制"(paint)这两步,合称为"渲染"(render)。

重排和重绘

网页生成的时候,至少会渲染一次。用户访问的过程中,还会不断重新渲染。

重新渲染,就需要重新生成布局和重新绘制。前者叫做重排(reflow),后者叫做重绘(repaint)。

需要注意的是,重绘不一定需要重排重排必然导致重绘

对于性能的影响

提高网页性能,就是要降低"重排"和"重绘"的频率和成本,尽量少触发重新渲染。

DOM变动和样式变动,都会触发重新渲染。但是,浏览器已经很智能了,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。

div.style.color = 'blue';
div.style.marginTop = '30px';

上面代码中,div元素有两个样式变动,但是浏览器只会触发一次重排和重绘。

如果写得不好,就会触发两次重排和重绘。

div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';

上面代码对div元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不立即重排。

一般来说,样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染。

offsetTop/offsetLeft/offsetWidth/offsetHeight
scrollTop/scrollLeft/scrollWidth/scrollHeight
clientTop/clientLeft/clientWidth/clientHeight
getComputedStyle()

所以,从性能角度考虑,尽量不要把读操作和写操作,放在一个语句里面。

// bad
div.style.left = div.offsetLeft + 10 + "px";
div.style.top = div.offsetTop + 10 + "px";

// good
var left = div.offsetLeft;
var top  = div.offsetTop;
div.style.left = left + 10 + "px";
div.style.top = top + 10 + "px";

一般的规则是:

重排何时发生

最小化重绘和重排

// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top  = top  + "px";

// good 
el.className += " theclassname";

// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
<ul id='fruit'>
    <li> apple </li> 
    <li> orange </li>
</ul>

如果代码中要添加内容为peachwatermelon两个选项,你会怎么做?

let lis = document.getElementById('fruit');
let li=document.createElement('li');
li.innerHTML='apple';
lis.appendChild(li);

let li = document.createElement('li');
li.innerHTML = 'watermelon';
lis.appendChild(li);

很容易想到如上代码,但是很显然,重排了两次,怎么破?
前面我们说了,隐藏的元素不在渲染树中,太棒了,我们可以先把idfruitul元素隐藏(display=none),然后添加li元素,最后再显示,但是实际操作中可能会出现闪动,原因这也很容易理解。

这时,fragment元素就有了用武之地了。

let fragment = document.createDocumentFragment();

let li = document.createElement('li');
li.innerHTML = 'apple';
fragment.appendChild(li);

let li = document.createElement('li');
li.innerHTML = 'watermelon';
fragment.appendChild(li);

document.getElementById('fruit').appendChild(fragment);

文档片段是个轻量级的document对象,它的设计初衷就是为了完成这类任务——更新和移动节点。文档片段的一个便利的语法特性是当你附加一个片断到节点时,实际上被添加的是该片断的子节点,而不是片断本身。只触发了一次重排,而且只访问了一次实时的DOM

参考文档:
高性能JavaScript 重排与重绘
网页性能管理详解

上一篇 下一篇

猜你喜欢

热点阅读