jQuery之documentFragment

進擊的小進進發表於2019-03-25

jQuery之documentFragment

前言: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>
複製程式碼

jQuery之documentFragment

我們可以看到,在 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" ) );
    }
}
複製程式碼

jQuery之documentFragment

(完)

相關文章