答案是: 标签内容不渲染、不执行脚本、不加载资源,仅作待命容器;必须通过 document.importnode(tmpl.content, true) 深克隆才能保留表单状态、事件绑定和 shadow dom 兼容性,直接 innerhtml 赋值会导致状态丢失。

<template></template> 标签本身不渲染、不执行脚本、不加载资源,它只是浏览器跳过的“待命区”。想靠它实现组件复用,必须配合 JS 实例化逻辑——写完 <template></template> 不等于组件就活了。
为什么直接 innerHTML = template.innerHTML 会丢状态
常见错误是把 <template></template> 当成字符串容器,用 innerHTML 直接塞进目标节点。这会导致:
-
<input checked>、<select></select>的当前选中态全部丢失 - 已绑定的事件监听器(如
addEventListener)不会自动迁移 - 内联
<script></script>或<style></style>不执行、不生效 - 若模板含
data-*属性用于后续 JS 查询,它们还在,但 DOM 节点引用关系已断
document.importNode 是克隆的正确姿势
document.importNode(tmpl.content, true) 是唯一能保留表单控件状态、正确处理 <select></select> 选项、兼容 Shadow DOM 的克隆方式。关键步骤:
- 获取模板:
const tmpl = document.getElementById('card-template') - 深克隆内容:
const fragment = document.importNode(tmpl.content, true) - 填充数据:遍历
fragment.querySelectorAll('[data-bind]'),按data-bind="title"映射到data.title - 文本更新用
node.textContent,富 HTML 更新用node.innerHTML(注意 XSS) - 挂载用
container.appendChild(fragment),别覆盖整个 container 的innerHTML
data-bind 比 class 更适合模板绑定
用 class="title" 做绑定点,容易和样式类名冲突;而 data-bind="title" 是语义明确、无副作用的标记方式:
- 支持多层级绑定:
data-bind="user.name"可解析为data.user.name - 避免 ID 冲突:禁止在模板里写
id="card-1",多次实例化时会重复 - 便于调试:DOM 中一眼看出哪些节点是绑定点,
querySelectorAll('[data-bind]')可直接提取 - 不干扰 CSS:BEM 类名可照常使用,
data-bind仅用于 JS 驱动逻辑
template 方案的真正边界在哪
它不提供封装性、无样式隔离、无属性监听、无生命周期钩子。所谓“传参”本质就是函数调用时传个对象,JS 手动映射到 DOM 节点。这意味着:
- 响应式更新要自己缓存节点,只改
textContent或dataset,不能每次重克隆 - 全局 CSS 会穿透进来,若需样式隔离,必须配合
Shadow DOM或构建时加 scoped class 前缀 - 复杂交互(如拖拽、动画控制)仍得靠外部 JS 管理状态,
<template></template>只负责结构快照 - 它适合静态内容、低频更新、轻量级复用场景;高频或强交互组件,该上 Web Components 或框架
最容易被忽略的是:模板内容一旦克隆进主 DOM,它就不再是“模板”,而是真实节点——后续任何修改都得手动同步,没有自动反射机制。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











