HTML模板实现列表项动态生成详解

作者:技术拾光

时间:2026-07-05

浏览:171

前端开发中,动态列表生成是常见需求,但稍有不慎就容易引发跨站脚本攻击(XSS)或性能瓶颈。以下是经过验证的实践原则:使用 createElement 配合 textContent 防范注入,模板字符串仅作为结构骨架;优先采用 template 标签加 cloneNode(true) 实现列表复用;高

文章目录

前端开发中,动态列表生成是常见需求,但稍有不慎就容易引发跨站脚本攻击(XSS)或性能瓶颈。以下是经过验证的实践原则:使用 createElement 配合 textContent 防范注入,模板字符串仅作为结构骨架;优先采用 template 标签加 cloneNode(true) 实现列表复用;高频渲染时利用 DocumentFragment 批量插入节点;若需模拟响应式列表,一个 renderList 函数进行全量重绘即可兼顾性能与简洁。

HTML模板实现列表项动态生成详解

模板字符串里怎么插变量才不会被当成纯文本

直接使用 innerHTML 拼接字符串时,一旦变量包含 < 或 ",DOM 结构就会被破坏,严重时甚至导致 XSS 漏洞。因此必须先转义再插入,或采用更安全的方案。

推荐组合 document.createElement 与 textContent:每个字段通过 textContent 单独赋值,天然规避注入风险。若确实需要渲染富文本,应先用 DOMPurify.sanitize() 过滤后再交给 innerHTML。

  • 避免:item.innerHTML = `
  • ${name}
  • ` —— 若 name 来自用户输入,等于为攻击者留下后门
  • 正确做法:const li = document.createElement('li'); li.textContent = name;
  • 模板字符串仅用于搭建结构骨架,不嵌入变量:const template = `
      `

    template 标签和 innerHTML 哪个更适合列表批量渲染

    是浏览器原生支持的“离线” DOM 容器,其内部内容不渲染、不执行脚本、不加载资源,非常适合作为可复用的列表项模板。相比拼接字符串再塞入 innerHTML,它更可靠,尤其在需要绑定事件或维持子节点状态时。

    需注意: 的内容必须通过 content.cloneNode(true) 复制出来,直接 appendChild 只会移动节点而非复制。

    • 定义模板:
    • 获取模板内容:const tmpl = document.getElementById('item-template').content;
    • 填充并追加:const el = tmpl.cloneNode(true); el.querySelector('.title').textContent = item.title; list.appendChild(el);
    • 务必传递 true 参数,否则克隆的仅是外层 节点自身

    循环渲染时如何避免重复创建 DOM 节点

    每次新增时调用 createElement 或克隆 本身并无问题,但高频操作(如滚动加载上千条数据)容易导致页面卡顿。关键在于“减少触发重排重绘”而非“减少创建”。

    将全部新节点先放入 DocumentFragment,最后一次性 append 到目标容器,能显著减少 layout 回流次数。

    • 错误示范:items.forEach(item => list.appendChild(createItem(item))); —— 每次 append 都可能引发重排
    • 正确做法:const frag = document.createDocumentFragment(); items.forEach(item => frag.appendChild(createItem(item))); list.appendChild(frag);
    • 若使用 ,同样先将克隆节点加入 frag,最后挂载

    Vue/React 风格的响应式列表在原生 JS 里怎么模拟

    原生 JavaScript 缺少响应式数据绑定,但可通过 Proxy 监听数组变化,配合模板自动更新 DOM。需注意 push、splice 等方法不会触发 set,需重写它们或使用 Reflect.set 加手动通知。

    更务实的方式是封装一个 renderList 函数,接收数据数组与渲染函数,每次数据变化时执行全量重绘——只要列表项不超过几百个,性能完全足够。

    • 简单封装:function renderList(container, data, renderItem) { container.innerHTML = ''; data.forEach(item => container.appendChild(renderItem(item))); }
    • 调用时传入编译好的节点:renderList(listEl, items, item => createItem(item));
    • 避免监听 Array.prototype.push —— 改写原型易引发兼容问题,且无法捕获字面量赋值

    实际项目中,最容易忽略的是模板节点的复用边界。 适用于结构固定、字段较少的列表;一旦涉及条件分支(如不同状态显示不同子组件)、嵌套列表或复杂交互,手写模板很快就会失控——此时应考虑使用轻量框架或自定义 HTML 元素。

    实用工具推荐

    电子包浆

    绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

    使用 ›
    图片压缩

    选择画质和输出格式,预览压缩前后的大小。

    使用 ›