主应用html结构不可被子应用覆盖或合并,document.documentelement和document.body是全局单例,子应用只能向主应用预留的容器节点渲染内容,所有head、body层级操作必须由主应用统一管控。

主应用 HTML 结构不能靠子应用“覆盖”或“补全”
微前端里不存在“HTML 合并”这种操作——document.documentElement 和 document.body 是全局单例,子应用无法新增、替换或接管它们。所谓“主从合并”,本质是主应用预留容器节点,子应用只往指定 div 里渲染内容,其余结构(如 、 层级样式、meta 标签)仍由主应用完全控制。
常见错误现象包括:
- 子应用在 mounted 阶段执行
document.title = '子应用',结果覆盖了主应用标题,且无还原机制 - 子应用动态插入
<link rel="stylesheet">到document.head,导致全局样式污染 - 主应用用了
body { margin: 0 },子应用又加body { padding: 1rem },最终生效的是后者(CSS 层叠顺序决定)
实操建议:
- 主应用应统一管理
<title></title>、<meta>、全局字体/重置样式,并禁止子应用直接操作document.head或document.body - 子应用若需修改页面标题,必须通过主应用暴露的 API(如
window.__setPageTitle)回调,由主应用做防抖、权限校验与还原兜底 - 所有子应用的
<link>和<style></style>必须拦截重写:qiankun 的modifyPublicPath+getPublicPath配合运行时样式隔离,MicroApp 的loadScript和loadStyle钩子可做资源路径修正与 scope 注入
Shadow DOM 容器不是“自动合并”,而是强制隔离边界
把子应用挂进 shadowRoot 并不等于“HTML 合并完成”,它只是划出一块 DOM+CSS+JS 的独立沙箱。关键点在于:shadowRoot.innerHTML = htmlString 是危险操作,会把内联 <style></style> 和 <script></script> 漏出到全局。
真正安全的注入流程必须分三步走:
- 用
DOMParser解析 HTML 字符串,分离出所有<style></style>节点内容 - 将每段样式文本转为
CSSStyleSheet实例(new CSSStyleSheet()),再赋值给shadowRoot.adoptedStyleSheets - 剥离所有
<script src="..."></script>,fetch 后在绑定好this/window/document的沙箱中执行;禁止执行内联<script>console.log(1)</script>
注意:adoptedStyleSheets 不支持 @import 和相对路径(如 url(./icon.png)),这些资源需主应用提前预加载或重写为绝对 URL。
data-* 属性不是合并开关,只是样式作用域锚点
手动给容器加 data-subapp="a",本身不会触发任何隔离逻辑。它只是给 CSS 选择器提供一个可写的前缀载体,是否生效,取决于你有没有配套的样式重写机制。
典型误用场景:
- 子应用写了
.btn { color: red },主应用加了div[data-subapp="a"] .btn { color: blue },但没确保子应用所有样式都经过前缀处理——第三方库(如 Ant Design)的.ant-btn依然全局生效 - qiankun 开启
strictStyleIsolation: true后,会在容器上自动加data-qiankun="sub-app-a",但如果你子应用用了@import引入外部 CSS,这部分规则不会被自动重写
实操建议:
- 不要手动维护
data-*,优先使用框架原生机制(qiankun 的experimentalStyleIsolation、MicroApp 的styleMode: "scoped") - 构建时用 PostCSS 插件(如
postcss-css-namespace)统一加前缀,但需确认所有子应用共用同一套配置,否则前缀不一致就白干 - 对
url()、@font-face等资源引用,必须额外做路径重写或改用 base64 内联,否则前缀方案会漏掉
iframe 是唯一真正“合并失败也能回退”的方案
当子应用 HTML 结构复杂、依赖大量全局样式或第三方脚本时,Shadow DOM 的适配成本可能远高于收益。此时 iframe 不是“退而求其次”,而是更鲁棒的选择——它天然隔离 DOM、样式、JS 执行环境、localStorage,且无需重写任何子应用代码。
代价也很明确:
-
postMessage通信有延迟,高频交互(如拖拽、滚动同步)需谨慎设计 - 弹窗(
alert、confirm)会被裁剪,需代理到父窗口 - SEO 友好性差,搜索引擎无法索引 iframe 内容
- 移动端键盘弹起时,iframe 内容可能被遮挡,需监听
resize+scroll补偿
关键提醒:iframe 的 src 必须指向同源地址(或配置 CORS + document.domain),否则无法跨域访问 contentWindow,postMessage 也收不到响应。非同源 iframe 只能单向通信(父 → 子),子应用无法主动通知父应用状态变化。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











