DocumentFragment(文档片段)简介&性能测试

2018-10-08

前言

感谢国家,这个月开始减税了,工资马上要多些啦。国庆回来的第一天,财务姐姐过来求助公积金的网站打不开,让我帮忙看下。浏览器是IE9,控制台报错:对象不支持“createContextualFragment”属性或方法。createContextualFragment是操作DocumentFragment的方法之一,返回一个DocumentFragment对象。古董浏览器(IE6-IE9)兼容性差,更换浏览器即可解决问题。又继续查了一下资料,可能是触发了EXT框架的一个bug,可插入以下pollyfill解决:

if ((typeof Range !== "undefined") && !Range.prototype.createContextualFragment) {
    Range.prototype.createContextualFragment = function(html)
    {
        var frag = document.createDocumentFragment(), 
        div = document.createElement("div");
        frag.appendChild(div);
        div.outerHTML = html;
        return frag;
    };
}


简介

DocumentFragment,文档片段,用于暂时储存一个或多个邻接的Document节点和它们的所有子孙节点,再统一插入到DOM树中。由于DocumentFragment不是真实DOM,修改DocumentFragment不会触发reflow(回流),仅在把DocumentFragment插入页面时触发一次reflow,提升渲染效率。
DocumentFragment原理

DocumentFragment也可用于文档的剪切、复制和粘贴,操作方便。


使用方法

创建:Document.createDocumentFragment()

//创建DocumentFragment
var fragment = document.createDocumentFragment();

//往DocumentFragment中写入内容
var browsers = ['Firefox', 'Chrome', 'Opera', 
    'Safari', 'Internet Explorer'];
browsers.forEach(function(browser) {
    var li = document.createElement('li');
    li.textContent = browser;
    fragment.appendChild(li);
});

//把DocumentFragment插入页面中
document.body.appendChild(fragment);


剪切文档:Range.extractContents()

//创建一个Range
var range = document.createRange(); 

//选择需要剪切的内容
range.selectNode(document.getElementsByTagName("div").item(0));  

//剪切的内容会在原文档中被删除,返回一个DocumentFragment对象
var documentFragment = range.extractContents();  

//把剪切的内容插入到页面中
document.body.appendChild(documentFragment);


复制文档:Range.cloneContents()

//创建一个Range
var range = document.createRange(); 

//选择需要复制的内容
range.selectNode(document.getElementsByTagName("div").item(0));  

//复制的内容会在原文档中保留,也会返回一个DocumentFragment对象
var documentFragment = range.cloneContents();  

//把复制的内容插入到页面中
document.body.appendChild(documentFragment);


性能对比

往页面里插入新的新的DOM,或者修改原有DOM的占位空间,都会触发reflow。因此我们构造一下大量插入div,并改变高度的操作:

// 循环次数
const cycle = 100000;

// 使用DocumentFragment
let fragment = document.createDocumentFragment();
for (let i=1; i<=cycle; i++) {
  let div = document.createElement("div");
  let text =document.createTextNode(`div${i}`);
  div.appendChild(text);
  div.style.height="30px";
  fragment.appendChild(div);
}
document.body.appendChild(fragment);

// 不使用DocumentFragment
for (let i=1; i<=cycle; i++) {
  let div = document.createElement("div");
  let text =document.createTextNode(`div${i}`);
  div.appendChild(text);
  div.style.height="30px";
  document.body.appendChild(div);
}

分别执行上述两段代码,使用Chrome的performance面板记录页面渲染时间。结果如下图:

DocumentFragment性能测试

可以看到,使用DocumentFragment可以有效减少渲染页面的时间,尤其是Rendering的时间。


总结

DocumentFragment可以储存若干节点,再一次性插入DOM树中,减少页面渲染时间。

听上去虽然不错,但古董浏览器兼容性不佳,而现代浏览器的内部优化,使得DocumentFragment的性能优势并不明显。现今的主流前端框架使用Virtual
 DOM(虚拟dom),DocumentFragment能发挥的场景已经不多了。

只有在需要剪切或复制大段内容的时候,才推荐使用它,其余情况下并不推荐使用。



本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。