传统操作DOM消耗性能的原因:DOM的回流(重排)和重绘
-
回流:当页面的布局或者几何信息发生变化,浏览器可能需要重新创建DOM树或者重新计算每一个元素在视口中的位置和大小(重新Layout),重新计算完成后,让浏览器重新渲染
- 回流必然会引发重绘
- DOM元素的增删改导致的DOM结构变化
- DOM的样式(例如:大小或者位置等)发生改变
- 浏览器窗口大小改变(视口改变)
- 页面第一次加载必然会有一次回流
-
重绘
- 元素样式发生改变,但是几何信息和结构信息没有改变,此时不需要回流,只需要浏览器把改变的元素重新渲染即可( color / background-color等)
需求:向#box盒子中动态插入5个span
// 当前代码会引发5次回流
for (let i = 1; i <= 5; i++) {
let span = document.createElement('span');
span.innerHTML = i;
box.appendChild(span);
优化思路:
- createDocumentFragment 文档碎片
let frag = document.createDocumentFragment();
for (let i = 1; i <= 5; i++) {
let span = document.createElement('span');
span.innerHTML = i;
frag.appendChild(span);
}
box.appendChild(frag);
frag = null;
- 字符串拼接
let str = ``;
for (let i = 1; i <= 5; i++) {
str += `<span>${i}</span>`;
}
box.innerHTML = str;
现代浏览器为了减少样式更改所引发的回流,都新增了“浏览器的渲染队列”机制
- 上一行代码是修改元素的样式,此时并没有直接通知浏览器去渲染,首先把它放置在浏览器渲染队列中,继续向下执行,把执行中遇到的修改样式的操作全部放置到浏览器的渲染队列中
- 如果不在有修改样式的操作,或者遇到了获取样式的操作(盒子模型或者获取样式),则中断向队列中存放的操作,把现有的先渲染一次(引发一次回流),继续向下执行代码 = >我们应该“读写分离”:把设置样式和获取样式的操作分离开