微前端 之 qiankun 踩坑指南

854 阅读3分钟

为什么是qiankun而不是iframe

  • 无法保持路由状态,刷新后路由状态就丢失(这点也不是完全不能解决,可以讲路由作为参数拼接在链接后,刷新时去参数进行页面跳转)
  • 完全的隔离导致与子应用的交互变得极其困难
  • iframe 中的弹窗无法突破其本身
  • 整个应用全量资源加载,加载太慢

qiankun原理

当路由切换的时候,去下载对应应用的代码,然后跑在容器里,qiankun 只是对 single-spa 的升级。 它升级了啥东西呢?第一个就是入口,改为了 html 作为入口,解析 html,从中分析 js、css,然后再加载,这个是 import-html-entry 这个包实现的。它是把 js 代码包裹了一层 function,然后再把内部的 window 用 Proxy 包一层,这样内部的代码就被完全隔离了,这样就实现了一个 JS 沙箱。就是 function 包裹了一层,所以代码放在了单独作用域跑,又用 with 修改了 window,所以 window 也被隔离了。

qiankun、wujie、micro-app 的区别主要还是实现容器(或者叫沙箱)上有区别,比如 qiankun 是 function + proxy + with,micro-app 是 web components,而 wujie 是 web components 和 iframe。

实现window隔离的原理

当我们在 JS 文件里有 window.a = 1 时,实际上会变成:

function fn(window, self, globalThis) {
  window.a = 1;
}

const bindedFn = fn.bind(window.proxy);

bindedFn(window.proxy, window.proxy, window.proxy);

那么此时,window.awindow 就不是全局 window 而是 fn 的入参 window 了。又因为我们把 window.proxy 作为入参传入,所以 window.a 实际上为 window.proxy.a = 1。这也正好解释了 qiankun 的 JS 隔离逻辑。

qiankun坑点

样式隔离

不启用样式隔离 父子,子子 应用的样式相互影响

解决方案
自己选择命名方案避免影响
qiankun方案-shadow dom
sandbox: {
  strictStyleIsolation: true,
}

主应用的样式和使用了strictStyleIsolation的子应用样式互不影响,但样式继承可以影响到

image.png

但是会导致弹窗的样式挂掉,因为弹窗默认是挂在 body 上的,也就不在 shadow dom 里了,那 shadow dom 里给它加的样式自然就不生效了。 那弹窗的样式问题怎么解决? 是通过通信机制把弹窗样式传过去么?那是不是改造成本又增加了? 所以 qiankun 的 shadow dom 的样式隔离方案是有问题的。

qiankun方案-scoped css
sandbox: {
  experimentalStyleIsolation: true,
}

它是怎么做的样式隔离呢?

借鉴了 scoped css 的思路。

也就是对所有样式加了一层 data-qiankun=“应用名” 的选择器来隔离:

image.png

这样其他应用的样式能影响子应用了

image.png

但是子应用的样式还是影响不了父应用,看上面的弹窗就知道了。

为什么呢?

因为子应用里所有的样式都加了 data-qiankun 的限制,那就影响不了子应用外部了,所以挂在 body 的弹窗还是挂掉了加不了样式。

有同学说,那支持声明 global 样式不就行了?

问题就在这,qiankun 并没有实现这个功能。 所以 qiankun 的 scoped css 的样式隔离方案也是有问题的。

而 react 和 vue 项目本身都会用 scoped css 或者 css modules 的组件级别样式隔离方案,这俩方案都支持传递样式给子元素、设置全局样式等,只是实现和使用方式不同。

现在的 vue、react 项目基本都做了组件样式隔离了,有点全局样式也是可控的,真没必要用 qiankun 的那个。

qiankun 的样式隔离方案比较坑,能不用就别用吧。

www.jsontots.com/