<template>标签本身不管理状态,需JS手动克隆并映射数据:必须用document.importNode(tmpl.content, true)保留表单状态,禁用innerHTML赋值以防丢失值和事件,用data-bind轻量绑定,挂载时用appendChild(fragment),样式隔离须依赖Shadow DOM。

<template> 标签本身不管理状态,也不自动注入数据——它只是浏览器跳过解析的静态容器。所谓“状态注入”,全靠 JS 手动做两件事:克隆内容、映射数据到 DOM 节点。没这一步,模板就是死的。
为什么 innerHTML = template.innerHTML 会丢状态
直接赋值会触发 DOM 重建,导致原生表单控件丢失当前值:<input checked> 变成未选中,<select> 回到默认项,<textarea> 内容清空。这不是 bug,是浏览器规范行为。
- 用
document.importNode(tmpl.content, true)替代cloneNode(true)或innerHTML,它能保留 checked/disabled/selected/value 等原生属性状态 - 不要在模板里写
id="submit-btn"——克隆多次后 id 重复,document.getElementById()返回第一个,不是你想要的那个 - 若需唯一标识,改用
data-id或生成随机 key:data-id="card-${Date.now()}"
data-bind 是最轻量的状态绑定约定
没有框架时,data-bind 是最可控的绑定方式:它不依赖正则替换或 AST 解析,只靠一次遍历 + 属性映射,安全且可调试。
- 支持文本填充:
<h2 data-bind="title"></h2>→node.textContent = data.title - 支持 HTML 注入(注意 XSS):
<div data-bind="content"></div>→node.innerHTML = DOMPurify.sanitize(data.content) - 支持属性绑定:
<img data-bind="src" src="">→node.setAttribute('src', data.src) - 不支持嵌套对象访问如
data-bind="user.name",必须扁平化传参:{ userName: user.name }
挂载时用 appendChild(fragment),别碰 innerHTML
用 innerHTML += 或 container.innerHTML = fragment.innerHTML 会销毁已有节点上的事件监听器、自定义元素实例、Web Component 生命周期钩子。
立即学习“前端免费学习笔记(深入)”;
- 始终用
container.appendChild(fragment)追加,或先container.replaceChildren(fragment)清空再插入 - 如果容器里已有其他组件,且它们绑定了事件,
innerHTML覆盖等于重置整个上下文 - fragment 是 DocumentFragment,插入一次即从文档流移除,不能重复 append —— 需要复用就得重新 importNode
样式隔离得靠 Shadow DOM,<template> 自己做不到
<template> 不提供任何作用域保护。哪怕你用了 c-card__header 命名,主应用的 .c-card__header { color: red } 依然生效。
- 真隔离必须调用
this.attachShadow({ mode: 'closed' }),然后把克隆后的 fragment 插入shadowRoot - Shadow DOM 下,CSS 不会穿透,但
:focus-visible、@media (prefers-reduced-motion)等系统级媒体查询默认不继承,需显式透传 - 构建时注入 CSS 的方案(如
<link rel="stylesheet">)在 Shadow DOM 中无效,必须用shadowRoot.appendChild(styleEl)动态插入
真正麻烦的不是怎么写模板,而是每次克隆后都要手动处理状态保留、事件绑定、样式作用域和 DOM 生命周期——这些细节不写进 JS,<template> 就只是个占位符。



















