You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建含多个子元素div的最优方案:createElement与innerHTML对比

安全认知补充

首先你的判断基本符合逻辑,但存在小缺陷:即便输入不是C端用户生成内容,如果输入来源包含运营后台配置、第三方接口返回、后端可编辑的配置项,依然有可能存在特殊字符或者被污染的风险,建议对插入模板的字符串做基础HTML转义,避免HTML结构被意外破坏。

另外你提供的第二个createDiv函数存在代码错误:模板字符串中直接使用了未声明的routing变量,运行时会直接抛出ReferenceError,正确写法是将该部分替换为${entry.routing ?? ''}。


问题逐一解答

1. 两种实现方案哪一种运行效率更高?

绝大多数场景下innerHTML方案性能更高,尤其是子元素结构复杂、批量生成大量节点时,差异更明显:

  • 浏览器的HTML解析器是C++实现的,innerHTML只需要一次JS到DOM引擎的上下文切换,就能一次性完成所有子节点的解析和创建
  • 多次调用createElement、appendChild会产生多次JS和DOM引擎的上下文切换开销,结构越复杂开销累积越明显
  • 仅当子元素数量极少(1-2个)时,两者性能差异在微秒级,几乎感知不到。

2. 业界实际开发中更推荐使用哪一种?

没有统一答案,完全按需选择:

  • 如果渲染完成后需要持有子元素引用做后续操作(绑定事件、动态修改属性、增删节点),更推荐createElement + appendChild方案,创建时就能直接持有引用,不需要额外做DOM查询。
  • 如果是纯静态渲染、结构固定、输入完全可控,更推荐innerHTML方案,代码更简洁易读,修改结构的成本更低。

3. 两种方案分别还有哪些需要注意的潜在问题?

createElement + appendChild 方案

  • 代码冗余度高,结构复杂时编写和维护成本极高,修改结构需要调整大量代码
  • 代码结构和最终DOM结构对应性弱,很难一眼从代码判断最终渲染效果

innerHTML 方案

  • 没有内置转义机制,输入中如果包含<、>、"、'、&等特殊字符会直接破坏HTML结构,极端情况会引发注入风险
  • 渲染完成后如果需要获取子元素引用,需要额外调用querySelector等DOM查询API,额外增加开销
  • 无法在创建节点的同时直接绑定事件,需要后续查询绑定或者用事件委托实现。

4. 是否存在比上述两种方案更高效、更合理的实现方式?

有两种纯原生的优化方案可以参考:

方案1:转义函数+模板字符串

封装一个极简的HTML转义函数,对所有插入模板的变量做转义,兼顾innerHTML的简洁性和安全性:

function escapeHtml(str) {
  return str.replace(/[&<>"']/g, char => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  }[char]))
}

// 改进后的createDiv
function createDiv (entry) {
  const div = document.createElement('div')
  div.classList.add('exchange')
  div.id = entry.exchange
  div.innerHTML = `
    <img class="logo-image" src="/static/img/${escapeHtml(entry.img)}.png" alt="${escapeHtml(entry.name)}">
    <a class="exchange-name" href="${escapeHtml(entry.url)}">${escapeHtml(entry.name)}</a>
    <span>${escapeHtml(entry.routing ?? '')}</span>
    <span class="price">${escapeHtml(entry.price)}</span>
  `
  return div
}

方案2:通用DOM创建工具函数封装

如果确实需要持有子元素引用,可以把公共属性赋值逻辑抽成工具函数,减少冗余代码:

function el(tag, props = {}, children = []) {
  const element = document.createElement(tag)
  Object.entries(props).forEach(([k, v]) => {
    if (k === 'class') element.className = v
    else if (k.startsWith('on')) element.addEventListener(k.slice(2).toLowerCase(), v)
    else element[k] = v
  })
  children.forEach(child => element.append(typeof child === 'string' ? document.createTextNode(child) : child))
  return element
}

// 改进后的createDiv
function createDiv(entry) {
  return el('div', { class: 'exchange', id: entry.exchange }, [
    el('img', { class: 'logo-image', src: `/static/img/${entry.img}.png`, alt: entry.name }),
    el('a', { class: 'exchange-name', href: entry.url }, [entry.name]),
    el('span', {}, [entry.routing ?? '']),
    el('span', { class: 'price' }, [entry.price])
  ])
}

这个方案既保留了DOM API的安全性和直接操作节点的能力,又让代码结构和最终DOM结构一一对应,可读性更高。


内容的提问来源于stack exchange,提问作者lenny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:57:00