如果引发一次重排(回流),必然会进行重绘;但是单纯只重绘,不一定会重排(回流);

重排/回流

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.png

1读写分离 尽量把所有改变样式的代码都放一块

  1. let box = document.querySelector('#box');
  2. // 当前触发三次重排======== 因为读取会刷新渲染队列
  3. box.style.width = '100px';
  4. box.style.height = '100px';
  5. console.log(box.offsetWidth); //刷新渲染队列
  6. box.style.position = 'absolute';
  7. box.style.left = '100px';
  8. console.log(box.offsetLeft); //刷新渲染队列
  9. box.style.top = '100px';
  10. box.style.background = 'pink'; */
  11. /* // 在新版的浏览器中,以下操作触发“一次”DOM重排:当代浏览器有“渲染队列机制”=====================
  12. box.style.width = '100px';
  13. box.style.height = '100px';
  14. box.style.position = 'absolute';
  15. box.style.left = '100px';
  16. box.style.top = '100px';
  17. box.style.background = 'pink'; */

2.读写分离-集中改变样式的两种方式

  1. box.className = 'box';
  2. box.style.cssText = 'width:100px;height:100px;background:pink;';

-3)元素的批量操作:文档碎片、模板字符串拼接等

  1. // 引发一次重排 模板字符串拼接
  2. let str = ``;
  3. for (let i = 1; i <= 5; i++) {
  4. str += `<span>
  5. ${i}
  6. </span>`;
  7. }
  8. document.body.innerHTML += str;
  9. //文档碎片 也只会引发一次重排
  10. let frag = document.createDocumentFragment(); //创建一个文档碎片(临时的容器用来存储DOM对象的)
  11. for (let i = 1; i <= 5; i++) {
  12. // 每一轮循环都把创建好的SPAN放在文档碎片中
  13. let span = document.createElement('span');
  14. span.innerHTML = i;
  15. frag.appendChild(span);
  16. }
  17. // 最后把文档碎片中的所有DOM元素,统一插入到页面中:引发“一次”重排
  18. document.body.appendChild(frag);
  19. /* // 此操作会引发“五次”重排:每一轮循环都会改变DOM结构=======
  20. for (let i = 1; i <= 5; i++) {
  21. let span = document.createElement('span');
  22. span.innerHTML = i;
  23. document.body.appendChild(span);
  24. } */

-4)开起GPU加速

  • 修改“transform”样式,不会引发重排「不会对整体重排,只会对当前这一层重新渲染」:因为修改transform样式,浏览器会自己新起一个文档流(层)去渲染,并不会对原始层中的元素位置、大小等信息产生改变
  • 我们平时修改样式,尽可能修改那些脱离文档流的{例如:position定位,我们修改top/left等信息}:虽然也会引发重排,但是渲染和计算的时候,也只是对当前这一层进行处理,其他层如果没有发生改变,则无需重新渲染

    -5)牺牲平滑度换取速度

    基于JS实现动画{定时器触发、requestAnimationFrame},我们一般会牺牲平滑度换取速度(性能);但是我们现在实现动画,基本上都是基于CSS3中的transition、animation实现,他们的性能会更好!