前言:documentFragment 在 jQuery 中的 buildFragment() 方法中总是用到,不了解它的含义话,读源码会比较困难,写文章以记之。
1、documentFragment
含义:
documentFragment 是一个轻量级的文档对象,能够提取部分文档的树或创建一个新的文档片段,换句话说有文档缓存的作用。
特征:
(1)documentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。(这一点在查找祖先节点大有用处)
(2)把一个 documentFragment 节点插入文档树时,插入的不是 documentFragment 自身,而是它的所有子孙节点。
这一特点与 React.Fragment 非常类似。(React.Fragment:https://www.jianshu.com/p/d4f...)
这使得 documentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。
2、<document>.createDocumentFragment() 方法
作用:
一般情况下,我们向 DOM 中添加新的元素或者节点,DOM会立刻更新。
如果向DOM添加 100 个节点,那么就得更新 100 次,非常浪费浏览器资源。
解决办法就是:
我们可以创建一个文档碎片(documentFragment),documentFragment 类似于一个小的 DOM,在它上面使用 innerHTML 并在 innerHTML 上插入多个节点,速度要快于 DOM(2-10 倍),
举个例子:
<body>
<script src="jQuery.js"></script>
<button id="Button1" onclick = "a1()">普通方式创建</button>
<button id="Button2" onclick = "a2()">documentFragment创建</button>
<div id="test1"></div>
<div id="test2"></div>
<script type="text/javascript">
function a1() {
console.time("普通方式创建")
for (let i = 0; i < 5000; i++) {
let op = document.createElement("span");
let oText = document.createTextNode(i);
op.appendChild(oText);
document.body.appendChild(op);
}
console.timeEnd("普通方式创建")
}
function a2() {
console.time("documentFragment创建")
let oFragmeng = document.createDocumentFragment(); //创建文档碎片
for (let i = 0; i < 5000; i++) {
let op = document.createElement("span");
let oText = document.createTextNode(i);
op.appendChild(oText);
oFragmeng.appendChild(op);
}
document.body.appendChild(oFragmeng); //最后一次性添加到document中
console.timeEnd("documentFragment创建")
}
</script>
我们可以看到,在 jQuery3.3.1 中的 buildFragment 方法中,运用了这一方法,来使得 $.append()
、$.after()
等方法更加快速高效。
//源码4857行-4945行
function buildFragment( arr, context, truefalse, selection ) {
let elem,tmp, nodes = [], i = 0, l = arr.length
// createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
//相当于document.createDocumentFragment()
let fragment = context.createDocumentFragment()
for ( ; i < l; i++ ) {
elem = arr[ i ];
if ( elem || elem === 0 ) {
tmp=fragment.appendChild( context.createElement( "div" ) );
tmp.innerHTML =jQuery.htmlPrefilter(elem)
jQuery.merge( nodes, tmp.childNodes );
}
}
// Remove wrapper from fragment
fragment.textContent = "";
//需要将i重置为0
i=0
while ( ( elem = nodes[ i++ ] ) ) {
fragment.appendChild( elem )
}
return fragment;
}
3、createElement() 和 createDocumentFragment 的区别
(1)innerHTML
- createElement 创建的元素可以使用 innerHTML;
- createDocumentFragment 创建的元素使用 innerHTML 不能达到修改文档内容的效果,只能作为一个属性
(2)DOM重复操作
- createElement 创建的元素添加到文档后可以重复操作;
- createDocumentFragment 创建的元素是一次性的,添加之后就不能操作了
因此,在 jQuery 源码的 domMainp() 方法中,运用了 createDocumentFragment 后,需要 clone 节点,来进行操作:
//源码5900行左右
if ( i !== iNoClone ) {
/*createDocumentFragment创建的元素是一次性的,添加之后再就不能操作了,
所以需要克隆iNoClone的多个节点*/
node = jQuery.clone( node, true, true );
console.log(i,iNoClone,'iNoClone5884')
// Keep references to cloned scripts for later restoration
if ( hasScripts ) {
// Support: Android <=4.0 only, PhantomJS 1 only
// push.apply(_, arraylike) throws on ancient WebKit
jQuery.merge( scripts, getAll( node, "script" ) );
}
}
(完)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。