重绘和回流

为什么总说jQuery操作DOM耗费资源,那他消耗了个什么资源,他是怎么消耗的呢???

咳咳,

其实很简单的一玩意😗😗了解玩法后再去看浏览器渲染原理就会轻松一点

PS:元素位置大小布局变化了就要回流,颜色背景等样式变了就要重绘。

  • 重绘(Repaint):浏览器的渲染引擎会根据各种样式计算每个盒子在页面上的大小与位置。(重新画图)

  • 回流(REflow):当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制。(就是布局)

 

回流

在页面初始渲染阶段,回流不可避免的会触发,就是一开始页面布局是个空的,而后面又添加了新的元素使页面布局发生变化,所以会回流。

当对 DOM 的修改引发了 DOM几何尺寸的变化(width、top、position比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来,这里就触发了回流

基本上边的也能够说清楚了,在看一点:回流就是要计算节点的位置和几何信息,当页面布局和几何信息发生变化的时候就需要回流,如:
     添加或删除可见的DOM元素
     元素的位置发生变化
     元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
     内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
     页面一开始渲染的时候(这避免不了)
     浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

重绘

当对 DOM的修改导致了样式的变化(color、box-shadowbackground-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘(或叫重排)

第一次渲染叫做绘制(paint),之后再重新渲染叫做重绘(Repaint)

减少重绘和回流

回流的成本比重绘高得多,所以开发中要尽量避免发生回流。

  • 使用transform替代top

  • 使用visibility替代display: none,前者只会引起重绘,后者会引发回流(改变了布局)。但是前者仍在文档流中占据空间,只是视觉效果上的不可见。

  • 不要把节点的属性值放在一个循环里当成循环里的变量

  • 不要使用table布局,可能很小的一个改动会造成整个table的重新布局。

  • 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的行为影响别的节点。比如video,浏览器会自动将该节点变为图层。(看下边合成和性能优化)

  1. 修改样式要尽量一次性改好

    1. 将多次改变样式属性的操作合并成一次操作

    2. 预先定义好class样式,然后修改DOM的className

  2. 尽量避免频繁的操作DOM

    1. 比如使用最小文档片段 DocumentFragment

    2. 比如要操作一个父元素和一个子元素,那最好就是直接操作父元素同时通过父元素操作子元素。

  3. 对某些元素定位使用absolute或者fixed。这样开销相对较小,不会对其他元素造成影响。

DOM操作

为什么总说jq需要操作dom虽然很灵活但是很消耗资源原因也就在这😫

因为DOM是属于渲染引擎中的东西,而JS又是JS引擎(V8)中的东西。当遇到JS操作DOM的时候,其实这个操作涉及到了两个线程之间的通信,那么势必会带来一些性能上的消耗。操作DOM次数一多,也就等同于一直在进行线程之间的通信,并且操作DOM可能还会带来重绘回流的情况,所以也就导致了性能上的问题

DocumentFragment

理解

含义:文档片段,它继承了Node的所有方法,对DOM操作性能非常好。😎其实就是个盛装元素的容器。

DocumentFragment是没有父节点的最小文档对象,常用于存储html和xml文档,它继承了Node,因此它有Node的所有属性和方法,完全可以操作Node那样操作DocumentFragment.。通常用来创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM Tree。因为文档片段存在于内存中,并不在DOM Tree中,所以将子元素插入到文档片段时不会引起页面回流。因此,使用文档片段通常会带来更好的性能。

//创建文档片段,他有三个node属性,它没有父元素
 var frag = document.createDocumentFragment();
 frag.nodeType;  // 11
 frag.nodeValue; // null
 frag.nodeName;  // #document-fragment
 frag.parentNode; // null

通常构建元素

1.通过createElement()函数创建
 var ul = document.getElementById("ulId");
 for (var i = 0; i < 30; i++) {
    var li = document.createElement('li');  
    li.innerHTML = "aaa" + i;
    ul.appendChild(li)
  }
 ​
 2、通过字符串拼接,比上一种要好,但毕竟是字符串的“死数据”,灵活度不太够
 var ul = document.getElementById("ulId");
 var ihtml = '';
 for (var i = 0; i < 30; i++) {
   ihtml += '<li>'+i+'</li>';
 }
 ul.innerHTML = ihtml;

而文档片段DocumentFragment就能够很好的解决以上两种方式消耗性能的弊端,看下边的栗子🤐

hello

<ul>
     <li> test1 </li>
     <li> test2 </li>
     <li> test3 </li>
   </ul>
 ​
  <script>
     const ul = document.getElementsByTagName('ul')[0];
     const lis = document.getElementsByTagName('li');
     
     // 1、创建fragment
     const fragment = document.createDocumentFragment();
     
     console.log(lis);       //HTMLCollection(3) [li, li, li]
     console.log([...lis]);  //(3) [li, li, li],如此处理过即可进行遍历
 ​
     // 取出所有li到fragment
     [...lis].forEach(li=>{
         fragment.appendChild(li)
     });
   
     // 更新fragment里所有li的文本
     [...fragment.children].forEach(li=>{
         li.innerText = 'baidu'
     });
   
     // 将fragment插入ul
     ul.appendChild(fragment)
     
   </script>

再看菜鸟教程

合成和性能优化

  • 在绘制的过程中可以将布局后的元素会知道多个合成图层中,这是浏览器的一种优化手段。

  • 默认情况下,标准流中的内容都是被绘制到同一个图层(Layer)中;

  • 一些特殊的属性,会创建一个新的合成层(CompositingLayer),并且新的图层可以利用GPU来加速绘制,因为每个合成层都是单独渲染的。

  • 分层能提高性能,但是同时需要进行一个内存管理,因此进行优化的时候不提倡过度使用。

可以形成新的合成层常见的一些属性:
     3D transforms
     video、 canvas、 iframe
     opacity 动画转换时
     position: fixed
     will-change:一个实验性的属性,提前告诉浏览器元素可能发生哪些变化
     animation 或 transition 设置了 opacity、 transform

完结

posted @ 2022-12-16 21:27  写点bug不过分吧  阅读(103)  评论(0)    收藏  举报