将字符串动态转换为DOM节点,在开发中常常遇到,尤为在模板引擎中更是不可或缺的技术。
字符串转换为DOM节点自己并不难,本篇文章主要涉及两个主题:
javascript
1 字符串转换为HTML DOM节点的基本方法及性能测试
2 动态生成的DOM节点添加到文档中的方法及性能测试html
本文的示例: 有以下代码段前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id='container'> <!-- 动态添加div <div class='child'> XXX</div> --> </div> </body> </html>
任务是编写一个JavaScript函数,接收一个文本内容,动态生成一个包含该文本的div,返回该Node。java
第一种方法,咱们使用document.createElement方法建立新的元素,而后利用innerHTML将字符串注入进去,最后返回firstChild,获得动态建立的Node。node
<script> function createNode(txt) { const template = `<div class='child'>${txt}</div>`; let tempNode = document.createElement('div'); tempNode.innerHTML = template; return tempNode.firstChild; } const container = document.getElementById('container'); container.appendChild(createNode('hello')); </script>
下面咱们看第二种方法浏览器
DOMParser 实例的parseFromString方法能够用来直接将字符串转换为document 文档对象。有了document以后,咱们就能够利用各类DOM Api来进行操做了。微信
function createDocument(txt) { const template = `<div class='child'>${txt}</div>`; let doc = new DOMParser().parseFromString(template, 'text/html'); let div = doc.querySelector('.child'); return div; } const container = document.getElementById('container'); container.appendChild(createDocument('hello'));
DocumentFragment 对象表示一个没有父级文件的最小文档对象。它被当作一个轻量版的 Document 使用,用于存储已排好版的或还没有打理好格式的XML片断。最大的区别是由于DocumentFragment不是真实DOM树的一部分,它的变化不会引发DOM树的从新渲染的操做(reflow) ,且不会致使性能等问题。app
利用document.createRange().createContextualFragment方法,咱们能够直接将字符串转化为DocumentFragment对象。dom
function createDocumentFragment(txt) { const template = `<div class='child'>${txt}</div>`; let frag = document.createRange().createContextualFragment(template); return frag; } const container = document.getElementById('container'); container.appendChild(createDocumentFragment('hello'));
这里要注意的是咱们直接将生成的DocumentFragment对象插入到目标节点中,这会将其全部本身点插入到目标节点中,不包含自身。咱们也可使用函数
frag.firstChild
来获取生成的div。
下面咱们来简单比对下上面三种方法的性能,只是测试生成单个节点,在实际使用中并不必定有实际意义。
先测试createNode。
function createNode(txt) { const template = `<div class='child'>${txt}</div>`; let start = Date.now(); for (let i = 0; i < 1000000; i++) { let tempNode = document.createElement('div'); tempNode.innerHTML = template; let node = tempNode.firstChild; } console.log(Date.now() - start); } createNode('hello');
测试100万个Node生成,用时 6322。
再来测试createDocument。
function createDocument(txt) { const template = `<div class='child'>${txt}</div>`; let start = Date.now(); for (let i = 0; i < 1000000; i++) { let doc = new DOMParser().parseFromString(template, 'text/html'); let div = doc.firstChild; } console.log(Date.now() - start); } createDocument('hello');
测试100万个Node生成,用时 55188。
最后来测试createDocumentFragment.
function createDocumentFragment(txt) { const template = `<div class='child'>${txt}</div>`; let start = Date.now(); for (let i = 0; i < 1000000; i++) { let frag = document.createRange().createContextualFragment(template); } console.log(Date.now() - start); } createDocumentFragment();
测试100万个Node生成,用时 6210。
createDocumentFragment方法和createNode方法,在这轮测试中不相上下。下面咱们看看将生成的DOM元素动态添加到文档中的方法。
被动态建立出来的节点大多数状况都是要添加到文档中,显示出来的。下面咱们来介绍并对比几种经常使用的方案。
下面咱们批量添加的方法都采用createDocumentFragment方法。
直接append方法,就是生成一个节点就添加到文档中,固然这会引发布局变化,被广泛认为是性能最差的方法。
const template = "<div class='child'>hello</div>"; function createDocumentFragment() { let frag = document.createRange().createContextualFragment(template); return frag; } // createDocumentFragment(); const container = document.getElementById('container'); let start = Date.now(); for (let i = 0; i < 100000; i++) { container.appendChild(createDocumentFragment()); } console.log(Date.now() - start);
上面的代码咱们测算动态添加10万个节点。结果以下:
测试1000个节点耗时20毫秒,测试10000个节点耗时10001毫秒,测试100000个节点耗时46549毫秒。
上面咱们已经介绍过DocumentFragment了,利用它转换字符串。下面咱们利用该对象来做为临时容器,一次性添加多个节点。
利用document.createDocumentFragment()方法能够建立一个空的DocumentFragment对象。
const template = "<div class='child'>hello</div>"; function createDocumentFragment() { let frag = document.createRange().createContextualFragment(template); return frag; } // createDocumentFragment(); const container = document.getElementById('container'); let fragContainer = document.createDocumentFragment(); let start = Date.now(); for (let i = 0; i < 1000; i++) { fragContainer.appendChild(createDocumentFragment()); } container.appendChild(fragContainer); console.log(Date.now() - start);
测试1000个节点耗时25毫秒,10000个节点耗时2877毫秒,100000个节点浏览器卡死。
简单了介绍了几种方法,并无什么技术含量。可是从动态添加节点来看,网上说的DocumentFragment方法性能远远好于直接append的说法在个人测试场景中并不成立。
DocumentFragment正确的应用场景应该是做为虚拟DOM容器,在频繁修改查询可是并不须要直接渲染的场景中。
更多精彩内容,请关注 微信订阅号“玄说前端”