Appearance
对于 DOM 的性能优化,主要是以下几点
- ECMAScript 与 DOM 在浏览器是独立实现的,所以通过 JS 去访问 DOM 都是需要过桥费的,会有很大的性能损耗。
最小化 DOM 的访问次数,尽量在 JS 端处理
优化的方法
- 缓存常用的 DOM
- innerHTML 与 createElement() 相差无几
- 复杂的 DOM 操作 使用 document.createDocumentFragement() 虚拟上下文
- el.cloneNode() 克隆节点操作 完成后再 replaceChild()
如果需要多次访问某个 DOM 节点,请使用局部变量存储起来。
小心处理 HTML 集合概念(HTML 集合是实时的),所以
我们常用的集合有哪些?
- NodeList
- NamedNodeMap
- HTMLCollection
优化的方法
- 把集合的长度缓存到一个局部变量中。
- 如果需要经常操作集合,请拷贝到一个数组中。
尽量使用速度更快的 API,如 querySelector() querySelectorAll()。 不要 document.getElementById("id").getElementsByTagName('li')这种。
留意重绘与重排。批量修改样式时,离线操作 DOM,使用缓存,并减少访问布局信息的次数。
动画中使用绝对定位,使用拖放代理。
使用事件委托来减少事件处理器的数量
// 1. ECMAScript 与 DOM 在浏览器是独立实现的,所以通过 JS 去访问 DOM 都是需要过桥费的,会有很大的性能损耗。 // 1.1 最小化 DOM 的访问次数,尽量在 JS 端处理 for(var i =5000;i>0;i--){ document.getElementById('id1').innerHTML += 'a'; } // 我们需要修改的地方就有 // 1. 不要频繁的访问 DOM,多次访问 DOM 缓存 DOM 对象。 var $id1 = document.getElementById('id1'); for(var i =5000;i>0;i--){ $id1.innerHTML += 'a'; } // 第二 不要频繁的去操作 DOM var $id1 = document.getElementById('id1'); var str = ''; for(var i =5000;i>0;i--){ str += 'a'; } $id1.innerHTML = str;
- 小心处理 HTML 集合概念(HTML 集合是实时的),所以 方法 document.getElementsByTagName() document.getElementsByClassName() document.getElementsByName() 属性 document.images,document.links, 元素的属性 el.attributes el.classList 例子: // 1. 死循环 var allDivs = document.getElementsByTagName('div'); for(var i =0;i<allDivs.length;i++){ document.body.appendChild(document.createElement('div')); }
// 2. 因为集合是实时的,所以我们每次读取 length 的时候,都需要重新查询,所以读取集合的 length 比数组的 length 慢得多。 // 第一步: 缓存 length var allDivs = document.getElementsByTagName('div'), divLength = addDivs.length; for(var i=divLength-1;i>=0;i--){ document.body.appendChild(document.createElement('div')); }
// 当我们需要对集合的每一个元素进行操作的时候,特别是大数据量的时候,最好每次缓存其 DOM var allDivs = document.getElementsByTagName('div'), divLength = addDivs.length cacheEl = null; var name = ''; for(var i=divLength-1;i>=0;i--){ cacheEl = allDivs[i]; name = cacheEl.nodeName; name = cacheEl.nodeType; name = cacheEl.nodeValue; }
// 4. 尽量使用速度更快的 API var el = document.getElementById("id").getElementsByTagName('li'); //这种最好使用 var el = document.querySelector('#id li')
- 留意重绘与重排。批量修改样式时,离线操作 DOM,使用缓存,并减少访问布局信息的次数。
什么是重排 当浏览器中的元素几何属性(宽高),位置发生了改变,浏览器需要使受影响的元素失效,重新构造渲染树。 什么是重绘 当元素的颜色,背景色等不影响宽高、位置的属性改变了,需要浏览器重新绘制一下这个元素就行。
重排产生情况
- 添加或者删除可见的 DOM 元素。
- 元素的位置发生改变。
- 元素的尺寸发生改变(外边距,边框,内边距,内容,宽高)
- 元素内容发生了修改。 文本修改 图片修改
- 页面渲染初始化
- 浏览器的窗口尺寸改变
浏览器通过队列话的方式去批量执行重排,但有些会强制刷新队列,并立即执行。(获取布局信息) offsetTop,offsetLeft,offsetWidth,offsetHeight scrollTop,scrollLeft,scrollWidth,scrollHeight clientTop,clientLeft,clientWidth,clientHeight getComputedStyle() currentStyle
怎么优化
- 样式的处理 通过 cssText 批量处理 style, className 批量处理 class
- 操作 DOM
- 使元素脱离文档流-> 处理元素 -> 添加回文档流 display:none; 文档片段 document.createDocumentFragment() cloneNode
- 获取布局信息
不建议
el.offsetTop = el.offsetTop + 2 + 'px'; 建议 var top = el.offsetTop; el.offsetTop = top + 1 + 'px';
- 动画问题 如我们常有的展开与折叠元素,这个就有大量的重排 解决方式 先让其脱离文档流,然后在添加入文档流中