template标签不渲染、不执行、不绑定,仅作离线仓库;必须用template.content.clonenode(true)克隆后填值插入,否则节点操作失效,且需用textcontent/value赋值、documentfragment批量插入、data-属性存元信息,slot无效。

template 标签本身不渲染、不执行、不绑定,它只是个“离线仓库”——想靠它动态渲染页面,必须用 JavaScript 主动克隆、填值、插入,没有自动机制。
为什么 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 - 需要保留 HTML 结构?先对用户输入做转义(如
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 树中,放心用
data- 属性能留,slot 不生效,别混用 Web Components 逻辑
想存 ID 或索引?data-id 可以保留在克隆节点上,但不会自动变成 JS 对象属性;想用 slot 分发内容?原生 template 不激活 slot 机制——它没挂载到 Shadow DOM,slot="xxx" 根本不解析。
- 存元信息:
cloned.querySelector('li').dataset.id = item.id -
slot在普通template渲染中完全无效,不要写<slot name="title"></slot>然后试图用querySelector('[slot="title"]')填值 - 如果真要用
slot,得配合自定义元素 +attachShadow,那已是 Web Components 范畴,和纯template渲染无关
最容易被忽略的是:每次循环都必须调用 cloneNode(true),否则所有项共用同一份节点引用,改一个就全变。这不是“写一次模板就能复用”的懒人方案,而是每次都要亲手克隆、亲手填、亲手塞——template 提供的是安全与语义,不是魔法。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











