將字符串動態轉換爲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容器,在頻繁修改查詢可是並不須要直接渲染的場景中。
更多精彩內容,請關注 微信訂閱號「玄說前端」