JS 沙箱与样式隔离:为什么子应用不能互相污染
本节目标
- 说清「JS 隔离」和「样式隔离」分别解决什么
- 理解快照沙箱和 Proxy 沙箱的原理差异
- 知道 qiankun 怎么隔离样式(Shadow DOM / scoped)
微前端最大的隐患是「共享一个 window、共享一张 DOM」。子应用 A 往 window 挂了个 chart,子应用 B 也挂了同名的,后者覆盖前者——这就是污染。隔离就是给每个子应用「造一个独立运行环境」。
JS 隔离(沙箱)。qiankun 早期用「快照沙箱」:
// 快照沙箱思路(理解用,qiankun 已默认用 Proxy 沙箱)
// 挂载子应用前:拍下 window 的快照
const windowSnapshot = { ...window }
// 子应用运行,随便改 window.xxx
window.chart = 'A 的图表'
// 卸载子应用时:恢复快照,把 window 还原成子应用进来前的样子
for (const k in windowSnapshot) window[k] = windowSnapshot[k]问题:快照沙箱不支持多个子应用同时运行(还原快照会互相踩)。现代 qiankun 用 Proxy 沙箱:给每个子应用造一个假的 window(Proxy),子应用读写全局变量都落在自己的 Proxy 上,互不可见、还能同时跑多个。
样式隔离。CSS 没有作用域,子应用 A 写了 .btn { color: red },子应用 B 的 .btn 就被染红。qiankun 两种手段:
- 严格样式隔离(实验性):用 Shadow DOM 包住子应用,CSS 天然作用域限定在影子树内。
- scoped(默认推荐):qiankun 给子应用所有选择器加前缀(如
div[data-qiankun="orderApp"] .btn),把样式局限在自己容器内。
// 基座 start 时开启隔离
import { start } from 'qiankun'
start({
sandbox: { strictStyleIsolation: true } // 用 Shadow DOM 严格隔离(实验性)
// 不开启 strictStyleIsolation 时,qiankun 默认走 scoped 加前缀方案
})名词解释
沙箱(Sandbox):给子应用造的「独立运行空间」,让它对全局变量(window)的读写不影响别的子应用。微前端靠沙箱实现 JS 隔离。
快照沙箱(Snapshot Sandbox):挂载前记录 window 快照、卸载时还原。简单但有「不能多应用并存」的硬伤。
Proxy 沙箱:用 Proxy 伪造每个子应用专属的 window,读写都落在各自副本上,支持多子应用同时运行,是 qiankun 现代默认方案。
Shadow DOM 样式隔离:把子应用装进 Shadow DOM,CSS 作用域天然封闭在影子树内,外部样式进不来、内部样式出不去(实验性特性,有兼容性取舍)。
课后练习
练习 1:为什么快照沙箱不支持「两个子应用同时显示」?
答案:它靠「还原 window 快照」来隔离,但 window 全局只有一份。A 挂载改了 window、还没卸载,B 又挂载再还原快照,会把 A 的改动抹掉。Proxy 沙箱给每个子应用独立副本才解决这个问题。
练习 2:strictStyleIsolation(Shadow DOM)有什么代价?
答案:Shadow DOM 会挡住「穿透样式」和某些依赖全局 DOM 的第三方库(如弹窗挂在 body 的组件可能失效),所以 qiankun 标它实验性。多数项目用默认的 scoped 加前缀方案更稳。
总结
隔离是微前端不「互相污染」的命门:JS 隔离靠沙箱(快照简单但不支持并存,Proxy 现代默认、各子应用独立 window);样式隔离靠 Shadow DOM 或 scoped 加前缀(默认 scoped 更稳,strictStyleIsolation 实验性但有兼容代价)。记住:能选 scoped 就别急着上 Shadow DOM。下一节讲子应用之间怎么通信。