DocumentFragment 的优化小知识

做者 : 混元霹雳手-ziksangjavascript

不管在作项目仍是学知识,当看到DocumentFragment 这个文档碎片的时候,你并不会去在乎他,每每就是这东西就能看的出你对Js的认识,和一个层级的化分,前天我看到一位小兄弟发了一篇文章,上面写着阿里十道面试题,里面有一个问题是这样的html

3.请把<body><p>第1行</p><p>第2行</p>...</body>(body之间有100个p元素)插入body里面,注意:须要考虑到性能问题。前端

对于想固然的想法vue

for(var i=0;i<100;i++)
       {
              var op=document.createElement("p");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              document.body.appendChild(op);
       }复制代码

咱们for大法进行循环建立一百次p无素,很显示,每次JavaScript对DOM的操做是一个很消耗性能的问题,页且每次进行appendchild插入节点的时候,会致使页面进行重绘(paining),对(layout)从新布局,那这个操做说明建立了100次节点,重绘了100次,暂且不谈,很显然,性能问题出来了。那面试的小伙是如何回答的这个问题的?java

对于这个回答,性能问题是解决了,是减小了dom的操做,若是我是面试官只给50分,那如何拿到更高的分数,其实本质上我想的是面试官确定考的不是经过这个知识点来解决此问题,我把代码重新写一遍jquery

var lis = "",ul = document.createElement("ul");
        //把li以字符串形式生成
        for(var i = 1; i <= 100; i++) {    lis += "<li>第" + i + "行</li>";}// 最后经过innerHTML插入ul里面
        ul.innerHTML = lis;
        //这里才操做dom,把ul插入到body
        document.body.appendChild(ul);复制代码

此方法只进行了一次dom操做,和一次页面重绘,是一个好办法,可是我以为这个方法有点硬凑的感受,没有把dom的知识点掌握全,并且这种写法对后续的拓展和维护性是不存在的,也没法封装成一个方法或者结合到任何框架中去面试

DocumentFragment

而后我想到这东西,不管是jquery仍是vue都使用了这玩意文档碎片来进行dom操做dom级别的优化浏览器

DocumentFragment接口表示没有父级的最小文档对象。它被用做轻量级版本,Document以像标准文档同样存储由节点组成的文档结构的片断。关键区别在于,因为文档片断不是实际DOM结构的一部分,它是一个虚拟的dom节点,存在于内存中,因此对片断所作的更改不会影响文档,致使回流,或者在进行更改时可能会发生任何性能影响。微信

一个常见的用途DocumentFragment是建立一个,在其中组装一个DOM子树,而后使用Node诸如appendChild()或(或insertBefore())之类的接口方法将该片断附加或插入到DOM中。这样作会将片断的节点移动到DOM中,留下空白DocumentFragment。由于全部的节点都被一次性插入到文档中,因此若是单独插入,则每一个节点只会触发一个回流和渲染,而不是每一个节点的潜在一个app

建立DocumentFragment ->document.createDocumentFragment()

var oFrag=document.createDocumentFragment();
       for(var i=0;i<100;i++)
       {
              var op=document.createElement("P");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              oFrag.appendChild(op);
       }
       document.body.appendChild(oFrag);复制代码

很明显,它有一种特殊的行为,该行为使得它很是有用,即当请求把一个DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的全部子孙节点。这使得DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操做。

此时咱们操做了两次dom,和一次重绘,为了建立文档碎片咱们多操做了一次dom换来了100次dom建立的优化,并无什么问题,也不用纠结这个问题。此时只是把documentFragment的子节点添加到了body节点里,并无把整个文档碎片加入进去。

当文档碎片插入完自动会被销毁碎片内容

这个细节我相信你们确定没有怎么关注过

var oFrag=document.createDocumentFragment();
       for(var i=0;i<100;i++)
       {
              var op=document.createElement("P");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              oFrag.appendChild(op);
       }
       document.body.appendChild(oFrag);
       for(var i=0;i<100;i++)
       {
              var op=document.createElement("P");
              var oText=document.createTextNode(i);
              op.appendChild(oText);
              oFrag.appendChild(op);
       }
       //这段代码中
       document.body.appendChild(oFrag);复制代码

你运行代码你会发现0-99,0-99,说明了什么?说明了只要文档碎片一但被插入后,会进行一个碎片回收,清除,可是文档碎片容器仍是存在的,咱们不用再次从新再进行一次dom操做来进行建立文档碎片

移除dom到文档碎片中

若是将文档中的节点添加到文档碎片中,就会从文档中移除该节点,也不会从浏览器再看到该节点,添加到文档碎片的新节点也不属于文档树

<html>
<head> <meta charset="UTF-8"> <title>v-circle</title> </head> <body> <div> <ul> <li>1</li> <li>2</li> </ul> </div> <script type="text/javascript"> var oFrag=document.createDocumentFragment(); var ul = document.getElementsByTagName('ul')[0] console.log(ul) oFrag.appendChild(ul) </script> </body> </html>复制代码

此时我把DOM树中的ul节点添加到文档碎片中,此时dom树中的ul节点也不见了,跑到了文档碎片中

你也能够很明显的看的出,文档碎片不存在于dom树中。因此上全部的操做不会重绘整个页面

jquery做者John Resig给了性能测试报告

虽然在2008年给出的报告,对于现代浏览器中,此性能差距不是很大,100次可能算不上什么,但对于前端大数据的时代里,仍是颇有必要的。

欢迎指正!!!!!!

想了解更多知识欢迎订阅个人掘金专栏

渣渣前端开发工程师,喜欢钻研,热爱分享和讲解教学请 微信 zzx1994428 QQ494755899

若是转载请标注出自@混元霹雳手ziksang