DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的孩子所代替。因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(reflow)(对元素位置和几何上的计算)。因此,使用文档片段document fragments 通常会起到优化性能的作用(better performance)。
在W3Cschool里说过
DocumentFragment 接口表示文档的一部分(或一段)。更确切地说,它表示一个或多个邻接的 Document 节点和它们的所有子孙节点。
DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。
不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作,尤其是与 Range 接口一起使用时更是如此。
可以用 Document.createDocumentFragment()
方法创建新的空 DocumentFragment 节点。
也可以用 Range.extractContents()
方法 或 Range.cloneContents()
方法 获取包含现有文档的片段的 DocumentFragment 节点。
功能强大,兼容良好,看看下图
中间插播一张图片,Document 对象的方法(来自w3cschool的手册)
开始之前我们先来个例子热身,告诉大家传统的插入DOM会引起什么样的性能问题
var elem = document.createElement('div'),
p = document.createElement('p');
p.innerHTML = 'elem ';
elem.appendChild(p);
document.body.appendChild(elem);
正如大家所知,这种代码非常糟糕(本身没有问题),只是每次插入数据都会引起页面重绘,如果是在循环之下短时间内多次重绘页面简直噩梦。相关知识有个大神总结过的博非常棒页面呈现、重绘、回流,不熟悉这方面的人强烈推荐看几遍.
这里简单概括一下:
回流: 因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。
重绘: 一些元素需要更新属性只是影响元素的外观,风格,而不会影响布局。
主流浏览器都会有些减少回流、重绘优化方面操作.
下面换成DocumentFragments写法看看
var oFragment = document.createDocumentFragment(),
elem = document.createElement('div'),
p = document.createElement('p');
p.innerHTML = 'frag ';
oFragment.appendChild(p);
elem.appendChild(oFragment);
document.body.appendChild(elem);
看起来变化不多,无非就是中间生成一个文档片段
var oFragment = document.createDocumentFragment();
生成dom先插入文档片段
oFragment.append(p);
最后一步前先把文档片段插入将被插入页面的父元素
elem.append(oFragment);
实际上这些写法我都不怎么常用,估计大多数人都爱用字符串插入,简单快捷,代码量都喜人
var str = '<div><p>str</p></div>',
document.body.innerHTML = str;
然后我们实际测试一下这三种方法效率到底怎样?
我们在页面放置三个列表元素,然后分别用三种方法遍历1000次生成看看;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="list1"></div>
<div id="list2"></div>
<div id="list3"></div>
<script type="text/javascript">
//获取元素
var list1 = document.getElementById('list1'),
list2 = document.getElementById('list2'),
list3 = document.getElementById('list3');
/* -----------------Element------------------------ */
console.time('Element用时 ');
var elem1 = document.createElement('div'),
i = 0;
for (; i < 1000; i++) {
var p = document.createElement('p');
p.innerHTML = 'elem ' + i;
elem1.appendChild(p);
}
list1.appendChild(elem1);
console.timeEnd('Element用时 ');
/* -----------------Element------------------------ */
/* -----------------Fragment------------------------ */
console.time('Fragment用时');
var oFragment = document.createDocumentFragment(),
elem2 = document.createElement('div'),
j = 0;
for (; j < 1000; j++) {
var p = document.createElement('p');
p.innerHTML = 'frag ' + j;
oFragment.appendChild(p);
}
elem2.appendChild(oFragment);
list2.appendChild(elem2);
console.timeEnd('Fragment用时');
/* -----------------Fragment------------------------ */
/* -----------------字符串------------------------ */
console.time('字符串用时 ');
var str = '<div>',
k = 0;
for (; k < 1000; k++) {
str += '<p>str' + k + '</p>';
}
list3.innerHTML = str + '</div>';
console.timeEnd('字符串用时 ');
/* -----------------字符串------------------------ */
</script>
</body>
</html>
实测数据如下:
次数 | Element用时 | Fragment用时 | 字符串用时 |
---|---|---|---|
1 | 14.9560546875ms | 18.69482421875ms | 3.69921875ms |
2 | 15.73388671875ms | 17.1826171875ms | 4.7451171875ms |
3 | 15.19482421875ms | 16.35205078125ms | 3.850830078125ms |
4 | 11.967041015625ms | 13.6240234375ms | 5.048828125ms |
可以看到实际差距大多数没有想象大,有时候还会略差,不过字符串写法一直稳定并且效率最高.
据说createDocumentFragment比直接操作DOM快70%,但是实践里没看到太明显的效果,有时候甚至还不如直接操作,不知道是不是哪里变量因素没考虑到,想了挺久,觉得原因可能是因为一我都是后面一次性插入到页面,实际测得都是一次影响DOM的效率,另一个就是因为结构简单,互相之间不影响布局.基于这种疑点我试试另一个测试方法如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="list1"></div>
<div id="list2"></div>
<script type="text/javascript">
//获取元素
var list1 = document.getElementById('list1'),
list2 = document.getElementById('list2');
/* -----------------Element------------------------ */
console.time('Element用时 ');
var i = 0;
for (; i < 1000; i++) {
var p = document.createElement('p');
p.innerHTML = 'elem ' + i;
list1.appendChild(p);
}
console.timeEnd('Element用时 ');
/* -----------------Element------------------------ */
/* -----------------Fragment------------------------ */
console.time('Fragment用时');
var oFragment = document.createDocumentFragment(),
j = 0;
for (; j < 1000; j++) {
var p = document.createElement('p');
p.innerHTML = 'frag ' + j;
oFragment.appendChild(p);
}
list2.appendChild(oFragment);
console.timeEnd('Fragment用时');
/* -----------------Fragment------------------------ */
</script>
</body>
</html>
直接生成文本元素然后插入页面或者插入DocumentFragment比较,这么看实际DocumentFragment也就充当一个容器的作用;因为字符串插入不能一概而论,去掉异常参考实测数据如下:
次数 | Element用时 | Fragment用时 |
---|---|---|
1 | 19.1669921875ms | 18.061767578125ms |
2 | 11.82568359375ms | 13.664794921875ms |
3 | 20.093017578125ms | 19.076171875ms |
4 | 15.6650390625ms | 19.4150390625ms |
现在看起来就比较偏向正常推测了,
目前也不好说什么,毕竟我只在Chrome下测试,数据量足够但是视图比较简单,
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。