jQuery之documentFragment

前言:documentFragment 在 jQuery 中的 buildFragment() 方法中老是用到,不瞭解它的含義話,讀源碼會比較困難,寫文章以記之。javascript


一、documentFragment
含義:
documentFragment 是一個輕量級的文檔對象,可以提取部分文檔的樹或建立一個新的文檔片斷,換句話說有文檔緩存的做用。html

特徵:
(1)documentFragment 節點不屬於文檔樹,繼承的 parentNode 屬性老是 null。(這一點在查找祖先節點大有用處java

(2)把一個 documentFragment 節點插入文檔樹時,插入的不是 documentFragment 自身,而是它的全部子孫節點。node

這一特色與 React.Fragment 很是相似。(React.Fragment:https://www.jianshu.com/p/d4f...瀏覽器

這使得 documentFragment 成了有用的佔位符,暫時存放那些一次插入文檔的節點。緩存


二、<document>.createDocumentFragment() 方法
做用:
通常狀況下,咱們向 DOM 中添加新的元素或者節點,DOM會馬上更新。
若是向DOM添加 100 個節點,那麼就得更新 100 次,很是浪費瀏覽器資源。app

解決辦法就是:
咱們能夠建立一個文檔碎片(documentFragment),documentFragment 相似於一個小的 DOM,在它上面使用 innerHTML 並在 innerHTML 上插入多個節點,速度要快於 DOM(2-10 倍),dom

舉個例子:ui

<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() 等方法更加快速高效。spa

//源碼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;
  }

三、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" ) );
    }
}

(完)

相關文章
相關標籤/搜索