前言: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
(2)DOM重複操做
所以,在 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" ) ); } }
(完)