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

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











