页面渲染过程
- 浏览器把HTML代码解析成一颗
DOM Tree
,每个元素标签都是DOM Tree
中的一个节点,根节点是Document
; - 在形成
DOM Tree
后,从DOM Tree
根节点开始,遍历每一个可见的节点,并找到合适的、匹配的CSSOM
规则,应用在节点上; DOM Tree
和CSSOM Tree
连接在一起形成Render Tree
;- 最终绘制出页面,显示在屏幕上
什么叫回流与重绘?
- 页面回流:当
render tree
中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而引起的页面重新渲染(或者叫作重新构建绘制); - 页面重绘:当
render tree
中的一些元素需要更新属性,但这些属性只会影响元素的外观、风格,而不会影响元素的布局,此类的页面渲染叫作页面重绘。
在网页生成的时候,至少会渲染一次。用户访问的过程中,任何对Render Tree
节点的操作都会引起重新渲染。
需要注意的是页面重绘不一定会引起回流,但回流一定会引起页面重绘。
常见的回流与重绘操作
DOM
节点的添加、删除;- 调整窗口大小(resize事件);
- 元素位置改变、元素尺寸改变(
width/height/padding/border/margin
); CSS
伪类的激活(如hover
);- 对样式的操作(不同属性有不同的影响)
- 元素样式属性的读取操作
开发过程中,提高性能的技巧
页面的回流、重绘在项目中是不可避免的,但过高、频繁的回流、重绘必定导致网页性能低下,影响客户体验感。所以,在开发过程中,我们应尽可能的避免回流、重绘,尽量少触发页面的重新渲染。
1. 尽量不要把读操作和写操作,放在一个语句里面。
// 下列操作只会造成一次回流、重绘
div.style.color = 'blue';
div.style.marginTop = '30px';
// 下列操作造成两次的回流、重绘
div.style.color = 'blue';
var margin = parseInt(div.style.marginTop);
div.style.marginTop = (margin + 10) + 'px';
在样式的写操作之后,有对元素进行
offset
、scroll
、client
、getComputedStyle()
属性读取操作,都会触发浏览器立即重新渲染。
// 坏的写法 --> 读写操作放同一个语句
div.style.left = div.offsetLeft + 10 + "px";
// 好的写法
var left = div.offsetLeft;
div.style.left = left + 10 + 'px';
2. 不要一条条地修改样式,而是通过改变class
、cssText
属性,一次性修改样式。
// 坏的写法
div.style.left = '10px';
div.style.top = '10px';
// 好的写法
div.className += 'newClassName';
div.style.cssText += 'background-color:red;font-size:20px;';
3. 尽可能使用Document Fragment
对象或者cloneNode()
,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。
// 坏的写法
for( var i = 0; i<2; i++ ){
var li = document.createElement('li');
li.textContent = i;
ul.appendChild(li);
}
// 好的写法
var fragment = document.createDocumentFragment();
for( var i = 0; i<2; i++ ){
var li = document.createElement('li');
li.textContent = i;
fragment.appendChild(li);
}
ul.appendChild(fragment);
4. 先将元素设置为display:none
,再对元素进行操作,最后恢复显示,这样只需要两次的重新渲染。
5. 尽可能地使用position;
,而不是float
来进行元素浮动。因为使用position
属性不用考虑它对其他元素的影响,回流的开销会比较小。
6. 尽可能地将display
属性设为可见,不可见属性的元素不会影响回流和重绘 (visibility
的元素只对重绘有影响,不影响回流)。
7. 使用虚拟DOM
的脚本库,比如React
、Vue
等。
8. 使用window.requestAnimationFrame()
、window.requestIdleCallback()
这两个方法调节重新渲染。
注意:window.requestAnimationFrame()
只能在IE10及以上使用,而window.requestIdleCallback()
在IE上是不支持的。这两个方法在阮一峰阮一峰的博客上有详细的介绍。
参考链接
- HTML5布局之路(著:刘国利)
- 网页性能管理详解(著:阮一峰)