直接用 template 标签写列表会失效,因为 template 默认不渲染且无自动绑定机制,{{item.name}} 或 v-for 不会被解析,需配合 js 手动克隆、填充、挂载并监听数据变化。

为什么直接用 template 标签写列表会失效
因为 template 默认不渲染,且内部没有自动绑定机制——你往里塞 {{item.name}} 或 v-for,浏览器根本不会解析,更不会响应式更新。它只是个“容器”,不是“模板引擎”。真要用,得配合 JS 手动克隆、填充、挂载,还得自己监听数据变化。
用 document.createElement + innerHTML 动态生成时的坑
常见错误是直接拼接字符串:el.innerHTML = `<li>${data.name}</li>`。这会导致 XSS(尤其 data 来自用户输入),且无法复用已有事件监听器;更糟的是,如果列表项里有 <input> 或 contenteditable,手动重写 innerHTML 会让焦点丢失、状态清空。
- 优先用
document.createElement+textContent处理文本内容 - 对富内容,用
element.setHTMLUnsafe(不存在)——别这么干,改用DOMPurify.sanitize()过滤后再插入 - 每次更新只 diff 差异节点,而不是全量替换:用
key字段标识唯一项,避免无谓重绘
如何让 template 真正参与响应式更新
核心是把它当“蓝图”,而不是“模板”。先在 HTML 里定义:
<template id="item-tpl"><div class="list-item"> <span class="name"></span><button class="delete">×</button> </div></template>然后用 JS 克隆、填值、绑定事件:
- 用
document.getElementById('item-tpl').content.cloneNode(true)拿到文档片段 - 用
querySelector找到.name节点,调textContent = item.name - 给
.delete绑 click 时,别用onclick属性,用addEventListener并传入闭包捕获item.id - 首次渲染后,把整个 fragment append 到父容器;后续更新只替换对应子节点,不 touch 其他 DOM
要不要引入 lit-html 或 hyperhtml
如果你项目已用 Web Components 或需要服务端同构,lit-html 是合理选择:它把模板编译成高效 patch 函数,支持属性绑定和事件指令(如 @click=${this.handleDelete})。但注意:lit-html 不处理数组 diff,默认是全量重排;要高性能列表,得配合 repeat() + key 函数。
- 小项目或仅需局部增强,手写
template+ 增量更新更轻量,没额外打包体积 - 若已有 Vue/React,硬套原生模板反而增加心智负担,不如用
v-for或map()直接生成 - 所有方案都绕不开一个事实:响应式 ≠ 自动刷新,关键在“谁持有数据引用”和“谁触发 re-render”
真正难的不是怎么写模板,而是决定在哪一层做 diff、何时触发更新、以及怎么让 key 稳定不漂移。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











