如果引发一次重排(回流),必然会进行重绘;但是单纯只重绘,不一定会重排(回流);
重排/回流
DOM的重排(回流):在第一次页面绘制完成后,如果我们修改了页面中节点的“位置、大小、结构等样式”,浏览器需要重新计算绘制规则(也就是重新生成Render-Tree),这个过程就是重排;重新生成Render-Tree后需要重新的绘制;
重绘
DOM的重绘:如果节点的位置、大小、结构等样式没有改变,只是改变了一些基础的样式(例如:改变了文字或者背景的颜色),此时无需重新生成Render-Tree,只需要重新Painting绘制即可!
为啥说操作DOM消耗性能?
- 因为操作DOM就有极大的几率会引发DOM的重排/重绘,所以性能消耗比较大!
- 再所以,当代项目开发,我们已经告别了直接操作DOM的时代(JQ也就没用了),
- 而是基于MVVM(vue)和MVC(react)等数据驱动模式进行开发
- 我们只需要操作数据,框架会根据数据帮助我们渲染视图和操作DOM
- 而框架内部操作DOM的时候,做了大量的优化处理,来提高性能!
性能优化:减少DOM的重排(回流)
-1)放弃直接操作DOM,使用vue/react/angular等数据驱动框架
-2)读写分离:把修改样式和获取样式的操作分离开,避免渲染队列的刷新
渲染队列机制
1读写分离 尽量把所有改变样式的代码都放一块
let box = document.querySelector('#box');
// 当前触发三次重排======== 因为读取会刷新渲染队列
box.style.width = '100px';
box.style.height = '100px';
console.log(box.offsetWidth); //刷新渲染队列
box.style.position = 'absolute';
box.style.left = '100px';
console.log(box.offsetLeft); //刷新渲染队列
box.style.top = '100px';
box.style.background = 'pink'; */
/* // 在新版的浏览器中,以下操作触发“一次”DOM重排:当代浏览器有“渲染队列机制”=====================
box.style.width = '100px';
box.style.height = '100px';
box.style.position = 'absolute';
box.style.left = '100px';
box.style.top = '100px';
box.style.background = 'pink'; */
2.读写分离-集中改变样式的两种方式
box.className = 'box';
box.style.cssText = 'width:100px;height:100px;background:pink;';
-3)元素的批量操作:文档碎片、模板字符串拼接等
// 引发一次重排 模板字符串拼接
let str = ``;
for (let i = 1; i <= 5; i++) {
str += `<span>
${i}
</span>`;
}
document.body.innerHTML += str;
//文档碎片 也只会引发一次重排
let frag = document.createDocumentFragment(); //创建一个文档碎片(临时的容器用来存储DOM对象的)
for (let i = 1; i <= 5; i++) {
// 每一轮循环都把创建好的SPAN放在文档碎片中
let span = document.createElement('span');
span.innerHTML = i;
frag.appendChild(span);
}
// 最后把文档碎片中的所有DOM元素,统一插入到页面中:引发“一次”重排
document.body.appendChild(frag);
/* // 此操作会引发“五次”重排:每一轮循环都会改变DOM结构=======
for (let i = 1; i <= 5; i++) {
let span = document.createElement('span');
span.innerHTML = i;
document.body.appendChild(span);
} */
-4)开起GPU加速
- 修改“transform”样式,不会引发重排「不会对整体重排,只会对当前这一层重新渲染」:因为修改transform样式,浏览器会自己新起一个文档流(层)去渲染,并不会对原始层中的元素位置、大小等信息产生改变
- 我们平时修改样式,尽可能修改那些脱离文档流的{例如:position定位,我们修改top/left等信息}:虽然也会引发重排,但是渲染和计算的时候,也只是对当前这一层进行处理,其他层如果没有发生改变,则无需重新渲染
-5)牺牲平滑度换取速度
基于JS实现动画{定时器触发、requestAnimationFrame},我们一般会牺牲平滑度换取速度(性能);但是我们现在实现动画,基本上都是基于CSS3中的transition、animation实现,他们的性能会更好!