直接 innerHTML 写入子应用 HTML 会污染样式,因为 shadowRoot.innerHTML = htmlString 绕过 Shadow DOM 封装机制,<style> 和 <script> 标签逃逸至全局作用域,前者影响主应用样式,后者在 window 上执行并操作 document。

为什么直接 innerHTML 写入子应用 HTML 会污染样式
因为 shadowRoot.innerHTML = htmlString 绕过 Shadow DOM 的封装机制,<style> 和 <script> 标签内容会逃逸到全局作用域——前者被浏览器当作普通 <style> 解析,影响主应用;后者在 window 上执行,document.body.classList.add('dark') 这类操作照常生效。
常见错误现象包括:子应用引入的 normalize.css 重置了整个页面字体大小;父应用的 [data-theme="dark"] .btn 意外覆盖子应用按钮;两个子应用都定义 .modal-overlay,z-index 失效。
- 不能靠“把子应用包进一个
div”解决——外部 CSS 仍可通过后代选择器穿透 -
scoped属性(如 Vue 单文件组件)只作用于当前组件内<style>,对子应用动态注入的<style>或第三方库无效 - @scope 规则尚未被任何主流浏览器支持,不可用
Shadow DOM 是唯一能切断样式链的原生方案,但必须用 closed 模式
attachShadow({ mode: 'closed' }) 是生产环境唯一推荐方式。mode 设为 'open' 等于放弃隔离能力:外部脚本可直接通过 el.shadowRoot 修改内部样式表或 DOM 节点。
注意:mode: 'closed' 下 el.shadowRoot 返回 null,但 DevTools 可通过 console.dir(el) 查看 #shadow-root 节点;CSS 注入必须手动完成,且不能依赖 <link rel="stylesheet"> 直接写入 light DOM。
立即学习“前端免费学习笔记(深入)”;
- 子应用所有 DOM 操作必须限定在自己的
shadowRoot内,否则逃逸节点不受样式保护 - React/Vue 渲染需显式指定
rootNode或调用createRoot(shadowRoot).render(),不能挂载到document.body -
adoptedStyleSheets是更现代的注入方式,但需检查兼容性(Safari 17.4+、Chrome 73+ 支持)
qiankun 的 strictStyleIsolation 为什么还会漏样式
这个模式只劫持动态插入的 <style> 和 <link rel="stylesheet">,对以下情况完全无能为力:
- 子应用 JS 中拼字符串后用
document.write或innerHTML注入样式(绕过劫持入口) - CSS-in-JS 库(如
styled-components)生成的<style>不走标准 DOM 插入流程 - 子应用 CSS 文件里用了
@import,父应用未递归解析并重写 - 主应用提前加载了全局
reset.css,子应用样式仍被button { margin: 0 }干扰
真正稳的做法是:主应用统一提供 scoped 基础重置(仅作用于自身容器),子应用禁用任何全局 reset,并约定只使用 CSS Modules 或构建时加前缀。
iframe 方案看似彻底,但代价极高
iframe 确实提供原生 JS + 样式双隔离,但实际项目中问题密集:
- 跨域时
subAppEntry必须同域,否则<script src="...">直接被 CORS 阻断或拒绝执行 -
<base href="%24%7BsubAppBase%7D">缺失会导致子应用内相对路径的fetch、图片、样式全部 404 - SEO 归零、无法共享状态、通信需
postMessage手动桥接,性能开销明显 - 子应用若用
document.querySelector或依赖window.location,多数 UI 库(如 Ant Design)会直接报错
Shadow DOM 和 iframe 都不是“开了就隔离”,关键在于是否切断所有逃逸路径——而最常被忽略的,是子应用对 document 的直接操作,以及未约束的全局选择器(body、button、* {})。



















