在 JavaScript 中操作 DOM 節點時,性能始終是開發者關注的焦點。常見的兩種方式是:使用原生 document.createElement、createTextNode 逐步構建節點並插入;另一種是將數據格式化為 HTML 字符串,再通過 innerHTML 一次性寫入。哪種方式性能損失更低?innerHTML 內部原理又是什麼?本文將深入分析並給出最佳實踐建議。
// 方式1:原生創建節點
var a = document.createElement('a');
a.href = 'https://www.baidu.com';
a.target = '_blank';
var text = document.createTextNode('this is baidu homepage');
a.appendChild(text);
document.body.appendChild(a);
這種方式每一步都直接操作真實 DOM 樹,會觸發多次重排與重繪,當節點數量大時性能開銷明顯。
// 方式2:數據格式化字符串
var data = {
content: "this is baidu homepage",
href: "https://www.baidu.com",
target: "_blank"
};
var tpl = '<a href="{#href#}" target="{#target#}">{#content#}</a>';
var tplEngine = function(data, tpl) {
return tpl.replace(/\{#(\w+)#\}/g, function(match, key) {
return typeof data[key] === 'undefined' ? '' : data[key];
});
};
document.body.innerHTML = tplEngine(data, tpl); // 注意:innerHTML 是屬性,不是方法
這種方式將 DOM 操作集中在一次 innerHTML 賦值中,瀏覽器會一次性解析字符串並構建子樹,減少重排次數,通常比逐個創建更快。
innerHTML 是一個屬性而非方法。當設置該屬性時,瀏覽器會:
這個過程與手動調用 createElement、createTextNode、appendChild 並不等價,因為瀏覽器內部會優化解析與構建流程,且只觸發一次重排。
當需要批量插入多個節點時,推薦使用 DocumentFragment:
var fragment = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
var div = document.createElement('div');
div.textContent = 'Item ' + i;
fragment.appendChild(div);
}
document.body.appendChild(fragment); // 只觸發一次重排
DocumentFragment 不在文檔樹中,操作它不會觸發重排;最後一次性將其掛載到真實 DOM,性能最優。
現代瀏覽器(Chrome、Firefox、Edge 等)對 innerHTML 和原生 DOM API 都進行了大量優化,兩者性能差距在小規模操作下已不明顯。但在大規模列表渲染、高頻更新場景下,DocumentFragment 或虛擬 DOM(如 React、Vue)仍是首選。
innerHTML 代碼簡潔、性能足夠。DocumentFragment 或虛擬 DOM 庫。