直接插入 dom 无法隔离样式,因为 仅是静态占位符,不创建作用域、不阻止 css 穿透、不限制 dom 查询;用 innerhtml 插入后所有样式和 id 落入 light dom,导致全局样式污染与 id 冲突,脚本也不执行。

为什么直接插入 DOM 无法隔离样式
因为 <template></template> 只是浏览器跳过解析的静态占位符,它不创建作用域、不阻止 CSS 穿透、也不限制 DOM 查询。你用 container.innerHTML = tmpl.innerHTML 插入后,所有 class、id、<style></style> 全部落在 light DOM 中,主应用的 .btn 或 [data-theme="dark"] .header 依然能命中子内容。
常见错误现象:
- 子应用模板里写
<div class="card">,结果被主应用全局 <code>.card { border: 1px solid red }覆盖 - 多个子应用共用同一
<template id="modal"></template>,挂载后出现重复id="modal",document.getElementById('modal')返回错误节点 - 模板含
<script>console.log('init')</script>,克隆插入后脚本完全不执行 - 获取模板:
const tmpl = document.getElementById('user-card') - 克隆内容:
const fragment = document.importNode(tmpl.content, true)(注意是tmpl.content,不是tmpl) - 绑定数据:遍历
fragment.querySelectorAll('[data-bind]'),按data-bind="name"映射到data.name,用textContent或setAttribute更新,禁用正则替换{{xxx}}(XSS 风险 + 不支持属性绑定) - 挂载:
container.appendChild(fragment),禁用innerHTML +=(会销毁已绑定事件) - 在自定义元素
constructor()中调用this.attachShadow({ mode: 'closed' })—— 延迟到connectedCallback就晚了,污染已发生 - 挂载时用
shadowRoot.appendChild(fragment),不是container.appendChild - 禁止模板内硬编码
id;改用data-id或动态生成唯一值,如data-id="${Date.now()}-${Math.random().toString(36).substr(2, 9)}" -
mode: 'closed'下el.shadowRoot返回null,但 DevTools 可见#shadow-root(需开启“Show user agent shadow DOM”) - 正确做法:用
DOMParser解析 HTML 字符串,提取所有<style></style>,转为CSSStyleSheet实例,再通过shadowRoot.adoptedStyleSheets = [sheet]注入(Chrome 73+ / Safari 16.4+ 支持) -
<script src="..."></script>需拦截后fetch,在沙箱中重绑定this/window/document到当前 shadow 上下文再执行 - 内联
<script>...</script>必须剥离,避免意外执行
如何安全克隆并填充内容
必须用 document.importNode(tmpl.content, true),而不是 cloneNode(true) 或 innerHTML —— 前者保留表单状态(checked、selected、textarea 值),后者会丢。
怎样让真正隔离?必须配 attachShadow({ mode: 'closed' })
<template></template> 自身零隔离能力。想切断样式穿透和 DOM 泄漏,唯一可靠路径是把它克隆后的 DocumentFragment 插入 closed Shadow DOM。
子应用 HTML 模板注入时,为什么不能用 shadowRoot.innerHTML = htmlString
直接赋值会绕过 Shadow DOM 封装机制:<style></style> 内容逃逸到全局,<script></script> 在全局上下文执行,既污染样式又引入 XSS 风险。
importNode 是状态保留底线,attachShadow({ mode: 'closed' }) 是样式隔离底线,漏掉任一环,所谓“组件化”就只是视觉分块而已。











