您好,登錄后才能下訂單哦!
前言
將字符串動(dòng)態(tài)轉(zhuǎn)換為DOM節(jié)點(diǎn),在開(kāi)發(fā)中經(jīng)常遇到,尤其在模板引擎中更是不可或缺的技術(shù)。
字符串轉(zhuǎn)換為DOM節(jié)點(diǎn)本身并不難,本篇文章主要涉及兩個(gè)主題:
1 字符串轉(zhuǎn)換為HTML DOM節(jié)點(diǎn)的基本方法及性能測(cè)試
2 動(dòng)態(tài)生成的DOM節(jié)點(diǎn)添加到文檔中的方法及性能測(cè)試
本文的示例: 有如下代碼段
<!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'> <!-- 動(dòng)態(tài)添加div <div class='child'> XXX</div> --> </div> </body> </html>
任務(wù)是編寫(xiě)一個(gè)JavaScript函數(shù),接收一個(gè)文本內(nèi)容,動(dòng)態(tài)生成一個(gè)包含該文本的div,返回該Node。下面話不多說(shuō)了,來(lái)隨著小編一起看看詳細(xì)的介紹吧。
1.1 動(dòng)態(tài)創(chuàng)建Node
1.1.1 innerHTML
第一種方法,我們使用document.createElement
方法創(chuàng)建新的元素,然后利用innerHTML將字符串注入進(jìn)去,最后返回firstChild,得到動(dòng)態(tài)創(chuàng)建的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>
下面我們看第二種方法
1.1.2 DOMParser
DOMParser 實(shí)例的parseFromString方法可以用來(lái)直接將字符串轉(zhuǎn)換為document 文檔對(duì)象。有了document之后,我們就可以利用各種DOM Api來(lái)進(jìn)行操作了。
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'));
1.1.2 DocumentFragment
DocumentFragment 對(duì)象表示一個(gè)沒(méi)有父級(jí)文件的最小文檔對(duì)象。它被當(dāng)做一個(gè)輕量版的 Document 使用,用于存儲(chǔ)已排好版的或尚未打理好格式的XML片段。最大的區(qū)別是因?yàn)镈ocumentFragment不是真實(shí)DOM樹(shù)的一部分,它的變化不會(huì)引起DOM樹(shù)的重新渲染的操作(reflow) ,且不會(huì)導(dǎo)致性能等問(wèn)題。
利用document.createRange().createContextualFragment
方法,我們可以直接將字符串轉(zhuǎn)化為DocumentFragment對(duì)象。
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對(duì)象插入到目標(biāo)節(jié)點(diǎn)中,這會(huì)將其所有自己點(diǎn)插入到目標(biāo)節(jié)點(diǎn)中,不包含自身。我們也可以使用
frag.firstChild
來(lái)獲取生成的div。
1.1.3 性能測(cè)試
下面我們來(lái)簡(jiǎn)單比對(duì)下上面三種方法的性能,只是測(cè)試生成單個(gè)節(jié)點(diǎn),在實(shí)際使用中并不一定有實(shí)際意義。
先測(cè)試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');
測(cè)試100萬(wàn)個(gè)Node生成,用時(shí) 6322。
再來(lái)測(cè)試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');
測(cè)試100萬(wàn)個(gè)Node生成,用時(shí) 55188。
最后來(lái)測(cè)試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();
測(cè)試100萬(wàn)個(gè)Node生成,用時(shí) 6210。
createDocumentFragment方法和createNode方法,在這輪測(cè)試中不相上下。下面我們看看將生成的DOM元素動(dòng)態(tài)添加到文檔中的方法。
1.2.0 批量添加節(jié)點(diǎn)
被動(dòng)態(tài)創(chuàng)建出來(lái)的節(jié)點(diǎn)大多數(shù)情況都是要添加到文檔中,顯示出來(lái)的。下面我們來(lái)介紹并對(duì)比幾種常用的方案。
下面我們批量添加的方法都采用createDocumentFragment方法。
1.2.1 直接append
直接append方法,就是生成一個(gè)節(jié)點(diǎn)就添加到文檔中,當(dāng)然這會(huì)引起布局變化,被普遍認(rèn)為是性能最差的方法。
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);
上面的代碼我們測(cè)算動(dòng)態(tài)添加10萬(wàn)個(gè)節(jié)點(diǎn)。結(jié)果如下:
測(cè)試1000個(gè)節(jié)點(diǎn)耗時(shí)20毫秒,測(cè)試10000個(gè)節(jié)點(diǎn)耗時(shí)10001毫秒,測(cè)試100000個(gè)節(jié)點(diǎn)耗時(shí)46549毫秒。
1.2.2 DocumentFragment
上面我們已經(jīng)介紹過(guò)DocumentFragment了,利用它轉(zhuǎn)換字符串。下面我們利用該對(duì)象來(lái)作為臨時(shí)容器,一次性添加多個(gè)節(jié)點(diǎn)。
利用document.createDocumentFragment()
方法可以創(chuàng)建一個(gè)空的DocumentFragment對(duì)象。
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);
測(cè)試1000個(gè)節(jié)點(diǎn)耗時(shí)25毫秒,10000個(gè)節(jié)點(diǎn)耗時(shí)2877毫秒,100000個(gè)節(jié)點(diǎn)瀏覽器卡死。
1.3 小結(jié)
簡(jiǎn)單了介紹了幾種方法,并沒(méi)有什么技術(shù)含量。但是從動(dòng)態(tài)添加節(jié)點(diǎn)來(lái)看,網(wǎng)上說(shuō)的DocumentFragment方法性能遠(yuǎn)遠(yuǎn)好于直接append的說(shuō)法在我的測(cè)試場(chǎng)景中并不成立。
DocumentFragment正確的應(yīng)用場(chǎng)景應(yīng)該是作為虛擬DOM容器,在頻繁修改查詢(xún)但是并不需要直接渲染的場(chǎng)景中。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。