真隔离唯一路径是iframe;Proxy沙箱因eval、Function构造函数等逃逸点无法真正隔离JS环境,仅防误操作;CustomEvent通信最轻量可控;Shadow DOM只隔离样式不隔离JS;沙箱本质是需工程约束的契约。

HTML 微前端里,主子应用之间没有天然隔离——window、document、history 全是共享的,所谓“沙箱”全是运行时模拟出来的。真隔离只有一条路:iframe;其他方案(Proxy、快照、Shadow DOM)都只能拦住一部分,逃逸点太多,得靠工程约束兜底。
为什么 Proxy 沙箱无法真正隔离 JS 执行环境
qiankun 这类框架用 Proxy 包一层 window,看起来变量读写被劫持了,但子应用仍能绕过:
-
eval('console.log(window.location)')直接穿透代理,访问原始window -
Function('return window')()构造函数逃逸,拿到真实全局对象 -
with (window) { ... }或Symbol.unscopables可绕过作用域代理 - 子应用若调用
document.write或动态appendChild到document.head,样式和脚本直接生效,沙箱完全失效
这些不是 bug,是 JavaScript 语言机制决定的——只要代码在主页面 JS 引擎里跑,就不可能 100% 隔离。所以别把 Proxy 当“安全沙箱”,它只是“防误操作”的轻量层。
iframe 是唯一 HTML 层级的真沙箱
iframe 的隔离由浏览器引擎保证,不是 JS 模拟出来的。同域下用 src="about:blank" 启动,再往 iframe.contentWindow.document 写入子应用 HTML,就能获得独立的:
立即学习“前端免费学习笔记(深入)”;
-
window、document、location、history实例 - 独立事件循环,
setTimeout和Promise不干扰主应用 -
history.pushState只影响 iframe 内部路由栈(配合 joint session history 可桥接到主应用)
注意:必须用 about:blank + 同域注入,不能直接 src 加载子应用 HTML,否则会触发跨域或 CSP 阻断;也别用 sandbox="allow-scripts" 跨域 iframe,通信成本高且能力受限。
CustomEvent 是最轻量、最可控的跨应用通信方式
比起 window.postMessage 或全局变量,CustomEvent 天然绑定在某个 DOM 节点上,生命周期清晰,不依赖沙箱是否完整:
- 主应用创建一个中央
EventTarget实例,暴露给所有子应用 - 子应用通过
bus.addEventListener('user-login', handler)订阅,bus.dispatchEvent(new CustomEvent('data-change', { detail }))发布 - 卸载时主应用直接
bus.removeEventListener或销毁实例,无残留监听器 - 不触碰
window或document,避免事件污染
如果子应用用了 Ant Design 等库弹窗挂到 document.body,或者自己动态插入 style 标签到 head,那通信可能“发出去了但 UI 不响应”——这不是通信失败,是 DOM 或样式脱离容器导致的渲染断连。
Shadow DOM 只隔离样式,不隔离 JS
Shadow DOM 的作用域限制仅对 CSS 生效,document、window、fetch 等 API 全部穿透。你把子应用挂进 shadowRoot,它照样能:
-
document.querySelector('body')查到主应用节点 -
window.parent.location.href = '/logout'直接跳转主站 - 执行内联
<script>localStorage.setItem('token', 'xxx')</script>,污染全局存储
所以 Shadow DOM 必须和 JS 沙箱(如 iframe 或严格约束的 Proxy)配合使用,单独用它等于只系了安全带却不关车门。
真正难的不是选哪种方案,而是让团队所有人理解:沙箱不是开关,是契约。无论用 iframe 还是 Proxy,子应用都必须承诺不调用 document.write、不裸绑 window.addEventListener、不往 document.body 插 DOM——这些约束没法靠框架自动 enforce,只能靠构建时 lint、运行时检测、上线前审计三道防线卡住。



















