1.节点修改
使用cloneNode在外部更新节点然后再通过replace与原始节点互换。
window.onload = function () {
var orij = document.getElementById('content');
//新的内容替换原有的内容
var clone = orij.cloneNode(false);
//在原有内容的基础上进行修改
//var clone = orij.cloneNode(true);
var list = ['foo','bar','baz'];
var content;
for(var i = 0; i < list.length; i++){
content = document.createTextNode(list[i]);
clone.appendChild(content);
}
orij.parentNode.replaceChild(clone,orij);
}
2.节点的添加
多个节点插入操作,即使在外面设置节点的元素和风格再插入,由于多个节点还是会引发多次reflow。
优化的方法是创建DocumentFragment,在其中插入节点后再添加到页面。
var btn = document.getElementById('btn');
btn.onclick = function () {
var list = document.getElementById('list');
var fragment = document.createDocumentFragment();
for(var i = 0; i < 5 ; i ++){
var li = document.createElement('li');
li.textContent = i;
fragment.appendChild(li)
}
list.appendChild(fragment);
}
3.CSS样式转换
如果需要动态更改CSS样式,尽量采用触发reflow次数较少的方式。
可以通过直接设置元素的className直接设置,只会触发一次reflow。
//css
.content{
width:200px;;
height:200px;
background-color: aqua;
}
.container{
background-color: pink;
width:200px;
height: 200px;
}
//HTML
<div class="content" id="content"></div>
//js
document.getElementById('content').className = "container";
4.减少DOM元素数量
在控制台的console中输入下面的语句查看DOM元素数量:
document.getElementsByTagName( '*' ).length
正常页面的DOM元素数量一般不应该超过1000。
DOM元素过多会使DOM元素查询效率,样式表匹配效率降低,是页面性能最主要的瓶颈之一。
5.DOM操作
-
DOM操作性能问题主要有以下原因:
- DOM元素过多导致元素定位缓慢。
- 大量的DOM接口调用。
- Javascript和DOM之间的交互需要通过函数API接口来完成,造成延时,尤其是在循环语句中。
- DOM操作触发频繁的reflow(layout)和repaint。
- layout发生在repaint之前,所以layout相对来说会造成更多性能损耗。
- reflow(layout)就是计算页面元素的几何信息。
- repaint就是绘制页面元素。
- 对DOM进行操作会导致浏览器执行回流reflow。
-
解决方法:
- 纯JAVASCRIPT执行时间是很短的。
- 最小化DOM访问次数,尽可能在js端执行。
- 如果需要多次访问某个DOM节点,请使用局部变量存储对它的引用。
- 谨慎处理HTML集合(HTML集合实时连系底层文档),把集合的长 度缓存到一个变量中,并在迭代中使用它,如果需要经常操作集合,建议把它拷贝到一个数组中。
- 如果可能的话,使用速度更快的API,比如querySelectorAll和firstElementChild。
- 要留意重绘和重排。
- 批量修改样式时,离线操作DOM树。
- 使用缓存,并减少访问布局的次数。
- 动画中使用绝对定位,使用拖放代理。
- 使用事件委托来减少事件处理器的数量。
事件委托示例
//HTML
<ul id="list">
<li id="go">go something</li>
<li id="do">do something</li>
<li id="sayhi">say hi</li>
</ul>
//JS
var list = document.getElementById('list');
list.addEventListener('click',function (event) {
switch(event.target.id){
case 'go':
alert('aaa');
break;
case 'do':
document.title = 'I change document title';
break;
case 'sayhi':
location.href = '//www.greatytc.com/p/91cb28114c82';
break;
}
})
5.DOM交互
在JAVASCRIPT中,DOM操作和交互要消耗大量时间,因为它们往往需要重新渲染整个页面或者某一个部分。
-
最小化现场更新
当需要访问的DOM部分已经被渲染为页面中的一部分,那么DOM操作和交互的过程就是再进行一次现场更新。- 现场更新是需要针对现场(相关显示页面的部分结构)立即进行更新,每一个更改(不管是插入单个字符还是移除整个片段),都有一个性能损耗。
- 现场更新进行的越多,代码完成执行所花的时间也越长。
-
多使用innerHTML
有两种在页面上创建DOM节点的方法:- 使用诸如createElement()和appendChild()之类的DOM方法。
- 使用innerHTML。
- 当使用innerHTML设置为某个值时,后台会创建一个HTML解释器,然后使用内部的DOM调用来创建DOM结构,而非基于Javascript的DOM调用。由于内部方法是编译好的而非解释执行,故执行的更快。
对于小的DOM更改,两者效率差不多,但对于大的DOM更改,innerHTML要比标准的DOM方法创建同样的DOM结构快得多。
6.回流reflow
- 发生场景。
- 改变窗体大小。
- 更改字体。
- 添加移除stylesheet块。
- 内容改变哪怕是输入框输入文字。
- CSS虚类被触发如 :hover。
- 更改元素的className。
- 当对DOM节点执行新增或者删除操作或内容更改时。
- 动态设置一个style样式时(比如element.style.width="10px")。
- 当获取一个必须经过计算的尺寸值时,比如访问offsetWidth、clientHeight或者其他需要经过计算的CSS值。
- 解决问题的关键,就是限制通过DOM操作所引发回流的次数。
- 在对当前DOM进行操作之前,尽可能多的做一些准备工作,保证N次创建,1次写入。
- 在对DOM操作之前,把要操作的元素,先从当前DOM结构中删除:
- 通过removeChild()或者replaceChild()实现真正意义上的删除。
- 设置该元素的display样式为“none”。
- 每次修改元素的style属性都会触发回流操作。
- element.style.backgroundColor = "blue";
- 使用更改className的方式替换style.xxx=xxx的方式。
- 使用style.cssText = '';一次写入样式。
- 避免设置过多的行内样式。
- 添加的结构外元素尽量设置它们的位置为fixed或absolute。
- 避免使用表格来布局。
- 避免在CSS中使用JavaScript expressions(IE only)。
- 将获取的DOM数据缓存起来。这种方法,对获取那些会触发回流操作的属性(比如offsetWidth等)尤为重要。
- 当对HTMLCollection对象进行操作时,应该将访问的次数尽可能的降至最低,最简单的,你可以将length属性缓存在一个本地变量中,这样就能大幅度的提高循环的效率。