不会自动同步状态,它只是静态容器,需手动克隆、绑定和更新;必须用 document.importnode(tmpl.content, true) 克隆,配合 shadow dom 实现真正的组件化隔离。

直接用 <template></template> 不会自动同步状态
很多人把 HTML 片段塞进 <template id="card"></template>,就以为它能“响应式”更新——其实不会。<template></template> 只是浏览器跳过解析的静态容器,里面的内容不执行脚本、不触发样式、不绑定事件,更不会监听 JS 对象变化。你改了 data.title,<template></template> 里的 {{title}} 不会变,除非你手动做替换或挂载。
常见错误现象:
- 模板里写
<div class="item">{{name}}</div>,但 JS 改了state.name,DOM 没反应 - 用
innerHTML = tmpl.innerHTML插入后,<input checked>的选中态丢失 - 多个地方复用同一
<template></template>,结果所有实例共享同一个id="form",document.getElementById('form')总返回第一个
document.importNode() 是克隆模板的底线操作
想保留原生行为(比如表单控件状态、<select></select> 选项、<textarea></textarea> 值),必须用 document.importNode(tmpl.content, true),而不是 cloneNode(true) 或字符串拼接。
实操要点:
- 永远从
tmpl.content克隆,不是tmpl.innerHTML - 克隆后得到的是 DocumentFragment,需遍历
fragment.querySelectorAll('[data-bind]')手动填充,避免正则替换{{xxx}}—— 容易 XSS,也处理不了data-bind="src"这类属性绑定 - 挂载时用
container.appendChild(fragment),禁用innerHTML +=,否则已绑定的事件监听器会被销毁 - 模板内禁止硬编码
id,改用data-id或动态生成唯一标识,例如data-id="${Date.now()}-${Math.random().toString(36).substr(2, 9)}"
状态变更后必须显式触发 DOM 更新
HTML 本身没有响应式机制,不像 React 或 Vue 那样自动 diff 和 patch。你改了状态对象,得自己决定怎么映射到 DOM——要么重 render 整个 fragment,要么只更新特定节点。
推荐做法:
- 封装一个
renderCard(state, container)函数,每次调用都container.innerHTML = ''再appendChild(importedFragment),简单粗暴但可控 - 如果性能敏感,可缓存关键节点引用,比如首次渲染后保存
const titleEl = container.querySelector('.title'),后续只改titleEl.textContent = state.title - 配合
store.subscribe()(如 Redux)或自定义事件监听,在状态更新后调用 render 函数,别依赖“数据变了 DOM 就该动”的错觉 - 注意:用
textContent替代innerHTML更新纯文本,防止意外执行 HTML 字符串
想真正隔离?必须搭配 attachShadow({ mode: 'closed' })
<template></template> 本身无作用域,哪怕你用了 data-app="cart",主应用的 [data-app="cart"] .btn 依然能穿透进来,Ant Design 的 .ant-btn 全局样式照样生效。
要切断样式和 DOM 查询链,必须走 Shadow DOM 路线:
- 在自定义元素构造函数中调用
this.attachShadow({ mode: 'closed' })——缺这句等于没建墙 - 克隆模板后,用
shadowRoot.appendChild(fragment)挂载,不是container.appendChild - 所有子应用 CSS 必须显式注入到
shadowRoot中,不能靠<link rel="stylesheet">自动加载(它只会加到 light DOM) - Shadow DOM 下无法用
document.getElementById查找内部元素,得用shadowRoot.getElementById或querySelector
真正的组件化不是把 HTML 拆成文件夹,而是让每个组件拥有自己的 DOM 边界、样式边界和状态边界——这三个边界缺一不可。多数人卡在“写了 <template></template> 就算组件化”这一步,实际连第一道墙都没砌起来。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











