在 开发过程中,或多或少都会遇到要操作dom的情况,而dom操作多多少少都会耗费一些性能,那今天我们就一起来看看在操作dom的时候有哪些性能优化方式吧:
1.选择性能更好的获取dom元素的方法
首先,我们一起来看看,如何使用js来获取dom元素:
- document.getElementById() / getElementsByClassName() / getElementByTagName()
- document.querySelector() / querySelectorAll()
以上方法,都可以帮助我们获取到dom元素,但getElementXX 和 querySelectorXX 选择哪个更能节约一些性能呢?我们一起来看看吧:
首先,我们需要知道它俩获取出来的DOM集合有什么区别:
让我们一起来看一下下面这两个例子分别打印出来的结果是什么 ?
在浏览器中执行一下,会发现使用getElementsByClassName获取的DOM元素最后打印的length为2 , 使用querySelectorAll 获取的DOM元素最后打印length为1。
这是因为 getElementXX 获取的是一个HTML集合,它是一种“假定实时态”,意味着底层文档对象更新,它也会跟着更新。
而 querySelectorXX 获取的是一个NodeList集合,它不会实时返回文档结构,只会保存当前获取时的DOM结构。
因为 getElementXX 的实时更新,它的性能会比querySelector差很多,如不稍不注意还容易导致死循环 :
所以获取DOM元素尽可能使用querySelectorXX,如果必须要用getElementXX也请浅拷贝 [...boxs] 一下,来避免它的“假定实时态”。
2.减少不必要的dom操作
我们应该都知道在浏览器实现js的操作和DOM的操作是分别独立的,js的实现名为JScript,位于jscript.dll文件中;DOM的实现存在另外一个库中,名叫mshtml.dll。
由于两者完全独立,如果两者需要沟通可以想象在他们之间建立一座“高架桥”,每次经过都需要收取“过路费”,是不是听起来就很耗费性能?所以我们应该尽可能减少经过这座“高架桥”。
尝试将DOM缓存起来:
如下图所示:第一种做法我们将经过两次“高架桥”,如果想要获取更多的属性那需要经过更多次“高架桥”。而第二种我们将DOM缓存起来,无论获取多少属性值都只需要经过一次“高架桥”。
尽可能将更新DOM操作统一起来:
如下图所示:通过下面的两个函数 innerHTMLLoop 和 innerHTMLLoop2 执行时间来看,将需要更新的内容先缓存起来,再一次性更新到页面上效率高很多。
还有一种场景,例如我们想一次性插入1000个<li/>节点到 ul.container 中,按照常规想法我们会这样做:
如果这样做我们就像container添加了1000次,相当于经过“高架桥”1000次,我们如何通过一次来更新1000次的操作呢?
实际上是有方案的:
我们可以通过生成一个虚拟的节点对象 document.createDocumentFragement 来让1000次更新先添加到这个虚拟节点,再统一更新到container上。
还有一种方案就是:
把 .contianer{display:none;} 再向cotianer.appendClind子元素,最后通过display:block展示出来。display: none的元素不会引起页面的重排 and 重绘,也会减轻性能。
我们可以根据自己的使用场景去选择使用方法。只要记住减少频繁的跟DOM操作即可。
3.减少页面重排 and 重绘次数
上面我们提到过更新、添加页面中的DOM元素,其实只要是页面中元素发生更改,都会引发页面的重排 or 重绘。
当然浏览器也不傻,重排很耗费性能,他不会在你一需要重绘的时候就立马帮你重排 and 重绘,它会缓存一个队列定期重排 and 重绘,但有一些属性会强制页面重排 and 重绘:
- offsetTop/Left/Width/Height
- scrollTop/Left/Width/Height
- clientTop/Left/Width/Height
所以我们应该尽量减少这类属性的使用,如果要使用也将它缓存起来不要频繁获取。
今天的内容就到这里结束啦,希望对大家有帮助。