qiankun 微前端的一些总结

date
Feb 7, 2023
slug
qiankun
status
Published
tags
微前端
summary
微前端的一些总结
type
Post
中 M微前端是基于 JS Entry和 qiankun这种区别比较大,今天有空看看 qiankun具体是怎么做。

一、路由特点

基于 Single-Spa,路由机制核心围绕「路由劫持 / 监听」实现子应用的加载、切换、卸载。主应用仅做路由监听和调度,子应用拥有独立的路由系统,本质是多应用的路由协同。
分 history和 hash两种模式,history中监听 popstate实现各应用间跳转,一般 会pushState/replaceState方法,达到监听所有history方法。
 

二、HTML Entry vs JS Entry

维度
HTML Entry(HTML 入口)
JS Entry(JS 入口)
核心形式
子应用暴露完整的 HTML 地址(如 http://localhost:3001/index.html
子应用暴露编译后的 JS 包地址(如 http://localhost:3001/main.js
加载逻辑
主应用加载子应用的 HTML,解析其中的 CSS/JS 资源并执行
主应用直接加载子应用的 JS 包,手动挂载 / 卸载子应用
隔离性
天然支持样式 / 脚本隔离(框架自动处理)
需手动处理样式 / 脚本隔离,成本高
接入成本
极低(子应用几乎无需改造)
较高(子应用需适配微前端规范)
样式隔离
  1. scoped css,为每个 子应用添加样式前缀
  1. 如需更彻底的隔离,可手动开启 Shadow DOM 模式,qiankun 会自动将子应用挂载到 Shadow Root 中。
  • 若子应用有依赖全局 DOM 的逻辑(如 document.body.appendChild),需适配 Shadow DOM(通过 shadowRoot 替代 document);
  • IE 浏览器不支持 Shadow DOM,需做好降级。
 
样式存在的一些问题
  • 子应用全局样式仍污染主应用
    • 原因:qiankun 的自动前缀对 body/html 等根选择器的处理有限;
    • 解决:子应用尽量避免写全局样式,或手动给全局样式添加前缀,如:
      • css
        /* 子应用内推荐写法 */ .app1-wrapper body { background: #fff; }
  • Shadow DOM 下第三方库样式失效
    • 原因:第三方库样式挂载到 document.head,不在 Shadow DOM 内;
    • 解决:qiankun 提供了 appendCssLinkToShadowDom 配置,自动将样式注入 Shadow Root:
      • javascript
        运行
        sandbox: { strictStyleIsolation: true, appendCssLinkToShadowDom: true, // 自动将子应用 CSS 注入 Shadow DOM }
  • 主应用样式覆盖子应用
    • 原因:主应用样式优先级高于子应用自动前缀样式;
    • 解决:子应用样式添加 !important(谨慎使用),或主应用给全局样式添加作用域限制。
 

三、js隔离

qiankun框架为了实现js隔离,提供了三种不同场景使用的沙箱,分别是快照沙箱、proxy代理沙箱
快照沙箱
快照沙箱是基于diff来实现的,主要用于不支持window.Proxy的低版本浏览器,而且也只适应单个的子应用。
激活沙箱时,将window的快照信息存到windowSnapshot中, 如果modifyPropsMap有值,还需要还原上次的状态;激活期间,可能修改了window的数据;退出沙箱时,将修改过的信息存到modifyPropsMap里面,并且把window还原成初始进入的状态。
代理沙箱
qiankun基于es6Proxy实现了两种应用场景不同的沙箱,一种是legacySandbox(单例),一种是proxySandbox(多例)。因为都是基于Proxy实现的,所以都称为代理沙箱。
proxySandbox不会污染全局window,支持多个子应用同时加载。
 

四、应用间通信

一般的通信方案是通过 URL 及 CustomEvent 来处理,但在一些简单场景下,基于 props 的方案会更直接便捷:
主应用创建共享状态:
import { initGloabalState } from 'qiankun'; initGloabalState({ user: 'kuitos' });
微应用通过 props 获取共享状态并监听:
export function mount(props) { props.onGlobalStateChange((state, prevState) => { console.log(state, prevState); }); };

© yujun 2021 - 2026