直接 innerhtml 挂载子应用 html 会逃逸沙箱,因浏览器解析不经过沙箱,内联和在全局执行,污染主应用样式与window;必须用domparser解析后挂载doc.body.innerhtml,并配合attachshadow({mode:'closed'})实现硬隔离。

为什么直接 innerHTML 挂载子应用 HTML 会逃逸沙箱
因为浏览器对 innerHTML 的解析不经过任何沙箱控制逻辑,所有内联 <style></style> 和 <script></script> 都在全局上下文中执行或注入。比如子应用 HTML 里有 <style>body { margin: 0 }</style>,它会直接改掉主应用的 body 样式;<script>window.myLib = {}</script> 会污染 window,绕过 JS 沙箱。
常见错误现象包括:
• 控制台报 Failed to execute 'querySelector' on 'Document'(脚本在全局执行但 DOM 尚未挂载)
• 子应用的 normalize.css 重置了整个页面字体大小
• 多个子应用都定义 .modal-overlay,后加载的覆盖 z-index 导致遮罩失效
- 必须用
DOMParser解析 HTML 字符串,得到标准Document对象,才能逐节点处理 -
doc.body.innerHTML是可挂载的安全 DOM 片段,不含原始执行上下文 - 不能用
cloneNode(true)替代document.importNode(tmpl.content, true)—— 前者丢表单状态(如input.checked)
如何用 DOMParser + Shadow DOM 构建硬隔离边界
仅靠 DOMParser 不够,它只解决解析问题;真正切断样式穿透和 DOM 查询链,必须配合 attachShadow({ mode: 'closed' })。这个调用必须在自定义元素的 constructor() 中完成,延迟到 connectedCallback 就已污染发生。
关键点:
• mode: 'closed' 下 element.shadowRoot 返回 null,外部无法访问,这是生产环境唯一有效模式
• shadowRoot.adoptedStyleSheets 只接受 CSSStyleSheet 实例,不能直接写入 <style></style> 标签
• adoptedStyleSheets 不支持 @import、相对路径 url(./img.png)、CSS 变量继承链
- 提取所有
<style></style>内容,用new CSSStyleSheet()创建实例,再调用sheet.replaceSync(text) - 拦截
<link rel="stylesheet">,fetch 后转为CSSStyleSheet注入,而非直接 append 到shadowRoot - 禁止
shadowRoot.innerHTML = htmlString—— 这会绕过全部封装机制,导致 XSS 风险和样式逃逸
子应用动态插入的 style/link 为何卸载时漏清理
微前端框架(如 qiankun)卸载时只移除容器节点及其子树,但子应用若在挂载后执行 document.head.appendChild(style) 或使用 Ant Design Modal 默认挂载到 document.body,这些节点不在容器内,沙箱完全失能。
典型逃逸路径:
• loadCSS('theme.css') 手动插入 <link> 到 document.head
• 组件库弹窗挂载到 document.body,其 <style></style> 标签脱离容器
• 子应用 HTML Entry 含全局 <style>body { background: #f0f0f0 }</style>,DOMParser 解析后挂载进容器,但卸载时未触发样式回收
- 强制子应用所有 UI 渲染限定在容器节点内,React/Vue 必须传
getContainer: () => container - 禁用子应用直接操作
document.head;如需加样式,走主应用提供的injectStyle(text)接口 - CI/CD 流水线应检查:所有
attachShadow调用必须含{ mode: 'closed' },否则阻断构建
HTML Entry 中资源路径不绝对导致 404 的根本原因
子应用独立运行时,./main.js 没问题;但被主应用 fetch 后,当前 base 是主应用域名,不是子应用构建时的 public path。浏览器按主应用 origin 解析相对路径,结果请求 https://main.com/./main.js,自然 404。
构建阶段必须设 publicPath: 'https://sub.example.com/'(不能是 'auto' 或 './');运行时兜底方案是主应用用 DOMParser 提取所有 src/href 属性,补全协议+域名。
- 子应用 Webpack/Vite 配置中
publicPath必须为完整协议域名 -
<base href>很重要,否则子应用内相对路径的fetch、图片、样式等会 404 - 避免内联脚本直接操作
window,如<script>window.xxx = ...</script>会跳过沙箱
最易被忽略的是:DOMParser 解析后得到的 doc.body 是干净片段,但后续所有 <script src></script> 加载、<link href> 注入、document.write 调用,都必须走沙箱重绑定逻辑——否则隔离墙就从内部被凿穿了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











