<template> 是 DOM 占位符,不渲染不执行;必须用 document.importNode() 克隆并手动填充数据,禁用 innerHTML 直接赋值,ID 不可硬编码,更新需局部操作而非重克隆。

<template> 不是组件,只是 DOM 占位符;想用它构建模块化界面,必须走 JS 实例化这一条关键路径——跳过这步,模板永远不渲染、不执行、不响应。
为什么 <template> 内容完全“不可见”
浏览器解析到 <template> 会直接跳过其子节点:不创建元素、不加载图片、不执行 <script>、不解析 data- 属性。常见错误是把它当 Vue 的 <template> 或 Web Component 的 <slot> 用,结果控制台没报错,页面却一片空白。
- 即使你写
<template id="card"><img src="avatar.jpg"><script>console.log('hi')</script></template>,DOM 中也查不到<img>,控制台不会输出hi -
innerHTML = template.innerHTML是危险操作:会丢失<input checked>的选中态、<select>的当前选项、<textarea>的用户输入 - 模板里写
{{title}}这类插值语法,纯 HTML + JS 不识别,必须手动替换,否则原样输出
安全克隆必须用 document.importNode(),不是 cloneNode()
cloneNode(true) 看似能复制内容,但它对表单控件状态、Shadow DOM 兼容性差;document.importNode(tmpl.content, true) 才是标准做法,能保留选中态、正确处理 <select> 和动态绑定。
- 获取模板:
const tmpl = document.getElementById('card-template') - 克隆内容:
const fragment = document.importNode(tmpl.content, true)(第二个参数true表示深克隆) - 不要操作
tmpl.content本身——它已被移出文档流,后续访问可能为空 - 若模板含
<style>或<script>,克隆后仍不会自动生效,需手动插入<head>或执行
填充数据时,textContent 比 innerHTML 更安全
模板中用 data-bind="title" 标记绑定点,JS 遍历替换时优先用 textContent,避免 XSS;只有明确需要 HTML 内容才用 innerHTML,且必须过滤或转义。
立即学习“前端免费学习笔记(深入)”;
- 文本内容:
el.textContent = data.title(自动转义,防 XSS) - 富文本内容:
el.innerHTML = DOMPurify.sanitize(data.content)(推荐引入轻量 sanitizer) - 禁止在模板内硬编码 ID,如
id="card-1"—— 多次复用会导致 ID 重复,破坏 CSS 选择器和getElementById - 绑定逻辑应封装成工厂函数,如
function createCard(data) { ... return fragment },返回真实 DOM 节点而非字符串
响应式更新不能靠重新克隆,得缓存并局部刷新
<template> 方案没有生命周期或响应式系统,所谓“更新”本质是手动操作已有节点。频繁克隆 + 替换性能差,且会丢失焦点、滚动位置等状态。
- 首次生成后缓存根节点,后续调用只更新
textContent、dataset或 class 列表 - 对表单控件,改用
el.value = data.value而非重写innerHTML - 若需监听属性变化(如
<my-card title="xxx">),必须自己实现观察逻辑,<template>本身不提供attributeChangedCallback - 复杂交互场景(如动画、焦点管理、键盘导航)建议直接上 Web Components,
<template>只适合静态或低频更新的模块
真正容易被忽略的是:模板内容是否已挂载、是否被多次克隆、是否混用了 id 或未清理旧事件监听器——这些不会立刻报错,但会在多实例、高频操作时突然崩掉状态。



















