因为操作DOM要收过路费。
把 DOM 和 JavaScript 各自想象成一个岛屿,它们之间用收费桥梁连接。——《高性能 JavaScript》
浏览器内核中的JS 引擎和渲染引擎是独立存在的,当我们用JS去操作DOM时,本质上是JS引擎和渲染引擎之间进行的“跨界交流”。
过“桥”要收费——这个开销本身就是不可忽略的。我们每操作一次 DOM(不管是为了修改还是仅仅为了访问其值),都要过一次“桥”。过“桥”的次数一多,就会产生比较明显的性能问题。因此“减少 DOM 操作”的建议,并非空穴来风。
#回流和重绘
JS操作DOM很慢,而且修改DOM还会引发它的外观、样式、大小、位置的改变,就会触发重流或重绘。
- 回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。
- 重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。
由此可以看出,重绘不一定导致回流,但是回流一定会导致重绘。
优化的关键,就是把重绘和回流的次数最小化。
#如何优化
知道了DOM慢的原因,我就可以逐步优化了。
#减少DOM操作,将多次操作合并为一次。
假如,我们要将1-1000渲染到box
里,如下:
<div id="box"></div>
我们首先想到的可能会这样:
代码语言:javascript复制for (var i = 1; i <= 1000; i ) {
document.getElementById('box').innerHTML = `<p>${i}</p>`
}
虽然最终效果是可以实现的,但是每次for循环都要过一次“桥”,一次两次还好,如果操作几十万、几百万的数据呢?页面就会明显感到卡顿..
能不能优化呢?当然可以,我们可以先设置一个变量,先去操作这个变量,最后一次插入DOM。
代码语言:javascript复制var str = ''
for (var i = 1; i <= 1000; i ) {
str = `<p>${i}</p>`
}
document.getElementById('box').innerHTML = str;
将多次DOM操作整合为一次,这也是一种优化的方式。
#使用DocumentFragment
将DOM多次修改,最后一次性插入到DOM中,这点在DocumentFragment中展现的淋漓尽致,关于DocumentFragment详细操作可以下一章。