template 标签不渲染、不执行、不绑定,仅作离线仓库;必须用 template.content.cloneNode(true) 获取可操作内容,填值优先用 textContent/value,批量插入需用 DocumentFragment 优化性能。
template 标签本身不渲染、不执行、不绑定,它只是个“离线仓库”——想用它做列表渲染,必须靠 JS 手动克隆、填值、插入。直接写 for 或插值语法(如 {{name}})完全无效,浏览器会原样忽略。
template.content.cloneNode(true) 是唯一可靠入口很多人卡在第一步:用 document.querySelector('template').innerHTML 拿不到内容,返回空字符串。这是因为 template 的真实内容被包裹在 DocumentFragment 里,只能通过 content 属性访问。
template.innerHTML 始终为空,永远别用template.textContent 返回所有文本(含换行缩进),但无法操作结构template.content.cloneNode(true),否则后续所有节点操作都失效true 参数是浅克隆,子元素事件监听器、表单状态、input 光标位置等全丢innerHTML,优先用 textContent 和 value
用 innerHTML 拼接字符串填值,等于主动引入 XSS 风险,还会意外销毁已绑定的事件监听器(比如模板里原本有 button,克隆后 onclick 就没了)。
el.querySelector('.title').textContent = data.title
el.querySelector('input').value = data.value,不是 textContent
text.replace(/&/g, '&')),再塞进 innerHTML
<script></script> 或 <style></style>?cloneNode(true) 不会执行脚本,也不会重复加载样式,别指望它“活”起来DocumentFragment,否则性能崩得快循环里每轮都 container.appendChild(cloned),等于让浏览器反复重排重绘。100 条数据可能就明显卡顿;500 条基本卡死。
立即学习“前端免费学习笔记(深入)”;
const frag = document.createDocumentFragment()
frag.appendChild(cloned)
container.appendChild(frag)
frag 是轻量中间容器,不能被 querySelector 查到,也不在 DOM 树中,放心用想存 ID 或索引?data-id 可以保留在克隆节点上,但不会自动变成 JS 对象属性;想用 slot 分发内容?原生 template 不激活 slot 机制——它没挂载到 Shadow DOM,slot="xxx" 根本不解析。
cloned.querySelector('li').dataset.id = item.id
cloned.querySelector('[slot="title"]') 这类写法,永远查不到目标customElements.define() + attachShadow(),那已是 Web Components 范畴,和纯 template 渲染无关最容易被忽略的是:每次克隆都得是全新副本。共用一个节点引用、漏掉 cloneNode(true)、或在循环外提前取了 content,都会导致只显示最后一项,或者 DOM 状态错乱。这不是 bug,是 template 的设计使然——它不维护响应式,只提供干净的原始结构。