前言:documentFragment 在 jQuery 中的 buildFragment() 方法中總是用到,不瞭解它的含義話,讀原始碼會比較困難,寫文章以記之。
1、documentFragment
含義: documentFragment 是一個輕量級的文件物件,能夠提取部分文件的樹或建立一個新的文件片段,換句話說有文件快取的作用。
特徵:
(1)documentFragment 節點不屬於文件樹,繼承的 parentNode 屬性總是 null。(這一點在查詢祖先節點大有用處)
(2)把一個 documentFragment 節點插入文件樹時,插入的不是 documentFragment 自身,而是它的所有子孫節點。
這一特點與 React.Fragment 非常類似。(React.Fragment:www.jianshu.com/p/d4f827527…
這使得 documentFragment 成了有用的佔位符,暫時存放那些一次插入文件的節點。
2、.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" ) );
}
}
複製程式碼
(完)