前言: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" ) ); } }
(完)