template 是惰性 DOM 片段仓库,内容解析但不渲染、不执行脚本、不加载资源;必须通过 cloneNode(true) 克隆 content 后插入,且需重设表单 ID/name/for 以确保功能正常。

template 标签不是占位符,也不是用来“藏 HTML 字符串”的容器——它是个原生的惰性 DOM 片段仓库,内容被解析但不渲染、不执行脚本、不加载图片。直接写进 HTML 就生效,但必须用 JavaScript 克隆后才能插入页面。
template.content 是唯一合法入口,innerHTML 永远为空
你永远拿不到 template.innerHTML 的内容,它恒为 "";template.children 也永远是空 NodeList。浏览器把真实内容封装在只读的 DocumentFragment 里,藏在 content 属性中。
- ✅ 正确:
const frag = document.querySelector('#item').content; - ❌ 错误:
template.innerHTML.replace(...)、template.firstElementChild、Array.from(template.childNodes) - ⚠️ 注意:
frag是“剪切语义”——首次appendChild(frag)后,frag就空了,第二次再 append 会静默失败
必须 cloneNode(true),别信 importNode 或浅克隆
cloneNode(true) 是目前最稳、最兼容的深克隆方式。虽然 document.importNode(template.content, true) 语义更准,但在 Safari 15.4 之前对 fieldset 或带 required 的表单控件有 bug,checkValidity() 可能返回 false 即使值完全合法。
- ✅ 推荐:
const instance = template.content.cloneNode(true); - ❌ 避免:
cloneNode(false)—— 只克隆空 fragment 容器,没子节点 - ⚠️ 表单场景必做:重设
id、name、for,否则多个实例 label 点击无效、提交字段覆盖 - ? 建议用
crypto.randomUUID().slice(0, 8)生成唯一 ID,别用Math.random()
template 不能随便放,嵌套位置有硬限制
template 必须直接位于 <body> 或 <head> 内,否则浏览器会静默移除或错位解析。尤其注意它不能出现在语义受限的父容器中:
立即学习“前端免费学习笔记(深入)”;
- ❌ 错误位置:
<table><template><tr><td></td></tr></template></table>→<template>被提到<table>外,<tr>还可能被自动包进<tbody> - ✅ 正确位置:
<body><template id="card"><div class="card">...</div></template></body> - ⚠️ 小陷阱:
<template>内部可以写语义非法结构(如<p><div>xxx</div></p>),因为不走标准渲染流程,只做解析挂起
别往 template 里塞 style/script,它们不会生效
<template><style>.x{color:red}</style><script>console.log(1)</script></template> 中的 <style> 不会应用,<script> 不会执行,控制台也不会报错——这是设计使然,不是 bug。
- ✅ 正确做法:样式写外部 CSS,用 class 控制;交互逻辑统一在克隆后绑定事件
- ❌ 不要依赖:试图靠内部
<script>初始化、或靠<style>隔离样式作用域 - ⚠️ 图片/iframe 不会预加载,但一旦克隆并插入 DOM,就会立即触发请求 —— 若需懒加载,应延迟赋值
src
真正容易被忽略的是:模板是否中立。如果里面写死 class="card card--tech" 或 <h1>最新技术动态</h1>,换到用户列表页就得重写——结构和业务语义耦合越紧,后期改起来越像在拆炸弹。



















