DOM操作的性能优化

DOM 为什么慢?

因为浏览器中 DOM 和 Javascript 是独立实现的,这个分离允许其他技术和语言比如 VBScript 能共享使用 DOM,也造成了性能的消耗

HTML 集合是数组吗?操作 HTML 集合有什么需要注意的?

HTML 集合对象不是数组,因为没有 push()或 slice()之类的方法,但提供了 length 属性,HTML 集合以一种“假定实时态”实时存在,当底层文档对象更新时,它也会自动更新。

var alldivs = document.getElementsByTagName('div')
for (var i=0 ; i<alldivs.length; i++) {
    document.body.appendChild(document.createElement('div'))
}

以上的代码是个死循环,因为循环的退出条件 alldivs.length 在每次迭代都会增加。而且也很慢,因为每次迭代都执行查询操作

因此操作 html 集合应该先将其拷贝到普通数组以减少访问次数。且访问集合元素的时候使用局部变量。

querySelectorAll()和 getElementsByTagName()有什么区别?为什么推荐使用第一种?

querySelectorAll()方法使用 CSS 选择器作为参数并返回一个 NodeList,不会返回 HTML 集合,因此不会对应实时的文档结构,避免了性能和可能逻辑上产生的问题

重绘与重排是什么?

浏览器下载完页面的所有组件,html,css,js,图片 之后会生成两个内部的组件结构,DOM 树和渲染树,其中 DOM 树表示页面结构,渲染树表示 DOM 节点如何显示

在 DOM 树和渲染树构建完成,浏览器就会开始绘制页面元素,当 DOM 的变化影响了元素的几何属性,浏览器需要重新计算元素的几何属性,同样其它元素的几何属性也会受到影响,浏览器会使原先的渲染树受到影响的部分失效并重新构造渲染树,这个过程称为重排,重排完成后将受影响的部分重新绘制到屏幕中成为重绘。

它们会导致 Web 应用的 UI 反应迟钝,所以尽可能减少这类过程的发生。

什么操作会强制渲染树队列的刷新?

offsetTop,offsetLeft…(获取偏移量)

scrollTop,scrollLeft…(获取滚动位置)

clientTop,clientWidth…

getComputedStyle(计算样式值)

重排何时发生?怎么减少重绘和重排带来的影响?

当页面的布局和几何属性发生变化的时候,例如删除,添加 DOM 元素;元素的位置改变;元素的尺寸改变;页面渲染器初始化;浏览器窗口发生改变。

减小重绘和重排带来的影响可以合并多次对 DOM 样式的修改,然后一次性处理。或者直接修改 class。

当需要对 DOM 元素进行一系列的操作时,可以通过是使元素脱离文档流->对元素进行多次改变->使元素带回文档流中,改过程只会出发两次重排,否则,对于元素进行的任何一次操作都会触发重排。

其中常见的办法是使用文档片断(document fragment) 在当前 DOM 之外构建一个子树,再把它拷贝回文档

关于事件委托

在前面的DOM 事件流中有总结过

总结

提高 DOM 操作的性能总结有以下方法:

最小化 DOM 访问次数

需要多次访问某个 DOM 节点应该使用局部变量储存引用

处理 HTML 集合建议把长度缓存到变量中,如果需要经常操作建议拷贝到数组中

尽可能使用速度更快的 API 如 querySelectorAll()和 firstElementChild

留意重绘和重排,不要在修改布局信息时去查询布局信息

动画中使用绝对定位,拖放代理

使用事件委托