什么是重排与重绘?
参考文章
网页性能管理详解 ——阮一峰
1. 网页生成的过程
网页生成的过程,大致可以分为五步:
HTML
代码转换为DOM
CSS
代码转换为CSSOM
( CSS Object Model )- 结合
DOM
和CSSOM
,生成一颗渲染树
( 包括每个节点的视觉信息 ) - 生成
布局
( layout ), 即将所有渲染树的所有节点进行平面合成
- 将
布局绘制
( paint ) 到屏幕上
2. 重排和重绘
2.1 重排
当渲染树
中部分或者全部元素的尺寸
、结构
或者属性
发生变化时,浏览器就会重新渲染部分或者全部文档,这个过程就叫重排
。每个页面最少发生一次
重排,那就是在页面第一次加载
的时候。
2.2 重排的触发条件
- 页面的第一次加载 ( 必定发生 )
- 浏览器的窗口大小发生变化
- 元素的内容发生变化
- 元素的尺寸、位置发生变化
- 元素的字体大小发生变化
- 激活 CSS 伪类 ( 例如 :hover )
- 查询某些属性或者调用某些方法
- 添加或者删除可见的 DOM 元素
2.3 重绘
当页面中某些元素的样式发生改变
,但是不会影响
其在文档流中的位置时,浏览器就会对该元素进行重新绘制,这个过程就是重绘
。
2.4 重绘的触发条件
- color、background 相关属性
- outline 相关属性
- border-radius、visibility、box-shadow
- 重排 ( 重绘不一定引起重排,但是重排一定会重绘 )
3. 怎么避免重排与重绘
- 避免使用层级较深的选择器,或其他一些复杂的选择器
- 设置动画效果时,使用 absolute 或者 fixed,使元素脱离文档流
- 避免使用 calc() 表达式,因为 CSS 表达式不仅是在页面呈现和调整大小时计算,而且在页面滚动时、鼠标移动时都要重新进行计算
- 多个 DOM 元素的读写操作,应分类存放,不要两个读操作直接加入一个写操作
- 避免频繁操作样式,最好将样式一次性改变,直接改变 className 或者使用 cssText 属性
- 避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有的 DOM 操作,最后再把它添加到文档
- 可以先把元素设置为 display:none, 操作结束后再将其显示出来。因为在 display:none 的元素上进行的 DOM 操作不会引起来重排和重绘
3.1 浏览器的优化机制
浏览器针对页面的重排与重绘
,进行了自身的优化:渲染队列
。
浏览器会将所有的重排与重绘操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔
,浏览器就会对队列进行批量处理,这样就会让多次
的重排、重绘变成一次
重排重绘。
但是,当获取布局信息的操作时,会强制队列刷新
,比如以下属性和方法:
offsetTop、 offsetLeft、 offsetWidth、 offsetHeight
scrollTop、 scrollLeft、 scrollWidth、 scrollHeight
clientTop、 clientLeft、 clientWidth、 clientHeight
getComputedStyle()
getBoundingClientRect
// 这些操作都需要返回最新的布局信息,因此浏览器不得不清空队列,触发重绘来返回正确的值。
4. documentFragment 是什么?
DocumentFragment
是一个保存多个 element
的容器对象 ( 保存在内存 ),当更新其中一个或者多个 element
时,页面不会更新。只有当 DocumentFragment
容器中保存的所有 element
更新后再插入到页面中才会更新页面。
由于 DocumentFragment
不会出现在文档树中,将 DocumentFragment
插入文档树中,相当于把它的子孙节点插入到文档树中,在频繁的 DOM
操作时,可以将 DOM
元素插入到 DocumentFragment
中处理,然后一次性的将所有的子孙节点插入到文档中。和直接操作 DOM
相比,将 DocumentFragment
节点插入到 DOM
树中,仅仅会触发一次页面的重绘,这样大大的提高了页面的性能
。
<ul id="list"></ul>
let ul = document.querySelector('#list')
let flag = document.createDocumentFragment()
for (let i = 0; i < 100; i++) {
let li = document.createElement('li')
li.innerHTML = i
flag.appendChild(li)
}
ul.appendChild(flag)