@(author: Jsaonwong) 以下是我的 个人博客 ,欢迎大家来作客并指正我文章中的错误!
事实上在写这篇文章之前想了许久,究竟要写关于哪个课题的文章,因为自己更希望写一些工作相关的课题,但是总觉得自己在工作中累计的各种经验太碎片化了,并不足以用文章输出出来,待我有空时精心整理之后再与大家分享吧,这里就先说一个大家平常都不怎么用到或者没用过的东西 DocumentFragment ;
可能许多童鞋都听说过这个东西,甚至也知道怎么使用它,但是我相信大部分的童鞋应该没有真正的去了解过它,追本溯源可以说是程序员最需要的一种特性,如果只是一味知道这个东西怎么用,或者是别人说这个东西好用就盲目地使用它,那么这恐怕并不是作者的本意。
没有真正了解过DocumentFragment这个东西的可以去 w3c 了解一下,看过之后大家就会了解到这个东西其实不是什么别的东西,正是我们耳熟能详的DOM对象。
它是一个概念比较简单却又很实用的DOM对象,w3c如是说:DocumentFragment 接口表示文档的一部分(或一段)。更确切地说,它表示一个或多个邻接的 Document 节点和它们的所有子孙节点,这要怎么理解呢!其实我是这么理解的,DocumentFragment它是一个文档碎片,轻量级的文档,能够提取部分文档的树或创建一个新的文档片段。
然而w3c下一句解释让我一开始有点懵逼,这句话是这么说的:“DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。”这句话又要怎么理解呢?
其实在所有节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段(documentfragment)是一种”轻量级“的文档,可以包含和控制节点,但不会像完整的文档那样占用额外资源。
而最有用的一段概念解释当为下面这一段:“即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作,尤其是与 Range 接口一起使用时更是如此。”
DocumentFragment的作用在最后这一段文字的解释中表达的淋漓尽致,它本身是不参与任何DOM操作的,它只是一个暂时缓存准备插入到dom的节点的“容器”。
江湖传闻,得DocumentFragment者得天下,听说使用这个api可以对DOM操作进行优化,并且性能会得到 大量 的提升,理论上来说如果情景是for循环10000次向body插入div的话,传统的使用appendChild会导致多次的回流和重绘,必然会影响页面的性能,然而事实如此吗?因此本人捧着一颗热忱的心前去试验了一把,结果却是让我大跌眼睛。
个人写的代码如下:(如果错误或不公平的地方吗敬请指正)
第一段是for循环append的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test for append's performance</title></head> <body> <div id="test1"> </div></body> <script>// per p appendvar start2 = new Date() for(var i = 0; i < 100000; i++) { var p = document.createElement('p'); var oText=document.createTextNode("test2"); p.appendChild(oText); document.body.appendChild(p);}var end2 = new Date() console.log('pappend use: ' + (end2.getTime() - start2.getTime()).toString()); </script> </html>
第二段是for循环append进文档碎片后再append到body的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test createDocumentFragment's performance</title></head> <body> <div id="test2"> </div></body> <script>// fregmentvar start1 = new Date(); var fragment = document.createDocumentFragment(); for(var i = 0; i < 100000; i++) { var p = document.createElement('p'); var oText=document.createTextNode("test2"); p.appendChild(oText); fragment.appendChild(p);}document.body.appendChild(fragment); var end1 = new Date() console.log('fragment use: ' + (end1.getTime() - start1.getTime()).toString()); </script> </html>
传统写法的平均耗时为:173.75ms
使用文档碎片的平均耗时为:169.25ms
综合两个的数据来看感觉性能上的差距并不大,难道说,DocumentFragment是浪得虚名吗?答案当然是:NONONO!
我们都知道,做性能测试,测试环境需要适应多样化五花八门的环境,在我这些代码中dom结构及其简单,而chrome浏览器的性能也是强大到不行,因此repaint和redraw的速度极快,因此在这种情况下做出的试验数据是难以让人信服的( 事实上在IE11的测试数据中传统写法的耗时甚至比使用文档碎片的耗时要更少 )。
难道说DocumentFragment仅仅只有防止因为回流和重绘的导致闪屏的作用吗?如果你这么说,我不服,DocumentFragment也不服,所以我们要再战。
那么究竟在什么情境下测试会比较科学一些呢,我沉思半晌突然灵光一闪,既然我在大繁星工作,为什么不在繁星的项目中进行测试呢,繁星整个项目的复杂度已经属于非常复杂的情景了,因此由此测试而得到性能数据是可以值得信服的。
具体的代码其实也就是上面代码块中的js代码,嵌入到了繁星主页面中,而且是window.onload之后方才执行,保证dom已经完全加载完毕;
传统写法的平均耗时为:1168.5ms
使用文档碎片的平均耗时为:686ms
噔噔噔!!果然,在我大繁星下的测试结果,传统写法与使用文档碎片的写法性能高下立判,而且两个值的比还会随着循环的次数越多,document越复杂而逐渐变大。这也证明越是复杂的页面,文档碎片的使用得到的性能提升也往往越大。
更多的数据大家可以自己动手测试一下,毕竟动了手才有发言权,现在最流行的一句话如是说,能动手尽量不BB!
那么接下来就是大家都比较关心的一个问题,这个东西的兼容性如何呢!!!让我们一起来看看
结果如下:( 源自 http://caniuse.com/#search=documentfragment) 综上所述:除了IE8及以下之外都支持这一API,请大家尽情使用createDocumentFragment吧,尤其是移动端更是无往不利。(PS:不过在正确的场景中使用才是正确的做法,任何api和方法都不应该被滥用,否则很有可能会弄巧成拙哦!)
这是本人第一次写博文,错误或许在所难免,如有不正确的地方欢迎指正,如果有严重错误的地方欢迎来喷!最后欢迎大家来我的博客逛一逛 http://jasonwong1991.github.io
最后的最后再说一句,下一篇将会讲关于createDocumentFragment与createElment之间的故事,敬请期待!