可能是你见过最完善的微前端解决方案 - 知乎 样式隔离 由于微前端场景下,不同技术栈的子应用会被集成到同一个运行时中,所以我们必须在框架层确保各个子应用之间不会出现样式互相干扰的问题。 Shadow DOM? 针对 "Isolated Styles" 这个问题,如果不考虑浏览器兼容性,通常第一个浮现到我们脑海里的方案会是 Web Components。基于 Web Components 的 Shadow DOM 能力,我们可以将每个子应用包裹到一个 Shadow DOM 中,保证其运行时的样式的绝对隔离。 但 Shadow DOM 方案在工程实践中会碰到一个常见问题,比如我们这样去构建了一个在 Shadow DOM 里渲染的子应用: const shadow = document.querySelector('#hostElement').attachShadow({mode: 'open'});shadow.innerHTML = ''; 由于子应用的样式作用域仅在 shadow 元素下,那么一旦子应用中出现运行时越界跑到外面构建 DOM 的场景,必定会导致构建出来的 DOM 无法应用子应用的样式的情况。 比如 sub-app 里调用了 antd modal 组件,由于 modal 是动态挂载到 document.body 的,而由于 Shadow DOM 的特性 antd 的样式只会在 shadow 这个作用域下生效,结果就是弹出框无法应用到 antd 的样式。解决的办法是把 antd 样式上浮一层,丢到主文档里,但这么做意味着子应用的样式直接泄露到主文档了。gg... CSS Module? BEM? 社区通常的实践是通过约定 css 前缀的方式来避免样式冲突,即各个子应用使用特定的前缀来命名 class,或者直接基于 css module 方案写样式。对于一个全新的项目,这样当然是可行,但是通常微前端架构更多的目标是解决存量/遗产 应用的接入问题。很显然遗产应用通常是很难有动力做大幅改造的。 最主要的是,约定的方式有一个无法解决的问题,假如子应用中使用了三方的组件库,三方库在写入了大量的全局样式的同时又不支持定制化前缀?比如 a 应用引入了 antd 2.x,而 b 应用引入了 antd 3.x,两个版本的 antd 都写入了全局的 .menu class,但又彼此不兼容怎么办? Dynamic Stylesheet ! 解决方案其实很简单,我们只需要在应用切出/卸载后,同时卸载掉其样式表即可,原理是浏览器会对所有的样式表的插入、移除做整个 CSSOM 的重构,从而达到 插入、卸载 样式的目的。这样即能保证,在一个时间点里,只有一个应用的样式表是生效的。 上文提到的 HTML Entry 方案则天生具备样式隔离的特性,因为应用卸载后会直接移除去 HTML 结构,从而自动移除了其样式表。 比如 HTML Entry 模式下,子应用加载完成的后的 DOM 结构可能长这样:
补充
补充:微前端要解决的核心难题之一是「子应用之间不互相污染」,需要从 CSS 隔离和 JS 隔离两个维度同时入手。
CSS 隔离方案对比:
- BEM / 命名空间前缀:约定前缀,编译时改写,简单但侵入性高,三方库难处理。
- CSS Modules / scoped:构建期 hash 化 class,作用域天然隔离,但子应用如果用了全局样式仍会污染。
- Shadow DOM:浏览器原生支持,绝对隔离;但 React/Vue 渲染到 shadow root 会有事件、样式继承、组件库等问题。
- Dynamic Stylesheet:子应用挂载时插入样式表,卸载时移除,借助 CSSOM 重构实现隔离。
- qiankun 的实验方案:基于 HTML Entry 解析后插入
<link>,卸载时移除整段子树。
JS 隔离(沙箱)对比:
- 快照沙箱:切换时保存
window快照,卸载时恢复;兼容性好但遍历 window 性能差。 - Proxy 沙箱:用 Proxy 代理
window读写;性能好,但需兼容 Proxy 环境。 - 多例沙箱:每个子应用独立 fakeWindow,不影响全局;最彻底但实现复杂。
- qiankun / micro-app / wujie 等框架都已封装好沙箱实现,开箱即用。
补充:现代微前端框架(如 wujie)结合 Web Components + iframe 双方案,兼顾隔离强度和性能。



