发布于 1 个月前 ,更新于 1 个月前 javascript

记录使用 qiankun 遇到的一些问题

老项目使用的是 Vue 2 + ElementUI ,而且项目中又集成很多个子系统。考虑到项目越来越庞大,构建时间也越来越长,再加上技术上难于升级,所以引入了 qiankun 的解决方案。

大致思路是主应用使用 Vue 3 + ElementPlus ,然后将老项目直接作为一个子应用,后期再从老项目中将子系统一个一个的拆出作为子应用。

qiankun 的 API 不多,看起来不难,但在使用中也遇到了不少的问题,本文记录了目前遇到的一些问题。

应用间的样式隔离

不使用 qiankun 的样式隔离方案( strictStyleIsolationexperimentalStyleIsolation )。

不使用的原因
  1. 主应用使用了 ElementPlus ,子应用使用了 ElementUI ,由于两个 UI 框架使用的 className 大都一样,如果使用 strictStyleIsolation 方案会导致主应用污染子应用的样式
  2. 如果使用 experimentalStyleIsolation 会导致子应用无法启动(应该是 Vue 不能找到挂载节点)
解决的思路
  1. 创建一个 ShadowDom( ShadowDom 挂载在页面中的某个元素上),再通过 document.createElement('div') 创建 containerDiv 和 appDiv 两个 DIV DOM
  2. containerDiv 作为 qiankun 的容器(对应 qiankun 的 container 参数),主要作用是通过 qiankun 把需要的资源加载进来
  3. appDiv 用于 Vue 挂载节点
  4. 将 containerDiv 和 appDiv 通过 appendChild 方法插入 ShadowDom 中

DOM 结构大致如下:

body
  ├ div
  │  ├ shadow dom (此区域内样式与外部隔离)
  │  │  ├ div (containerDiv,装载资源,如:css、js)
  │  │  ├ div (appDiv,Vue 实例的挂载节点)
子应用弹窗的处理
问题的原因

弹窗基本上是通过 document.body.appendChild 实现,这样就导致弹窗的 DOM 是插入到主应用中,导致弹窗没有样式。

解决的思路

通过 Proxy 代理 document.body.appendChild 方法(相当于做了一层拦截),如果插入的 DOM 符合某个规则(如:DOM 的属性、class 、id 等等),则仍然插入到 body 中,否则一律通过 shadowDom.appendChild 将弹窗的 DOM 插入到 Shadow 中。

function proxy(shadowDom) {
  if (!document.body.appendChild.isProxy) {
    document.body.appendChild = new Proxy(document.body.appendChild, {
      apply(target, thisArg, node) {
        if (node[0].classList.contains('el-overlay')) {
          target.apply(thisArg, node);
        } else {
          shadowDom.appendChild(node[0]);
        }
      },
      get: isProxy,
    });
  }
}

其他一些方法也可以使用代理方式去解决,比如 getComputedStyle。当然也可以使用 Object.defineProperty 去拦截。

这个解决方法虽然能解决问题,但总感觉不妥。

子应用字体图标处理
问题的原因

在 Shadow DOM 中使用 @font-face 是不生效的。

解决的思路
  1. 在主应用中加载字体
@font-face {
  font-family: "iconfont";
  src: url('//at.alicdn.com/t/2851331_ac75kuoita5.woff2') format('woff2'),
        url('//at.alicdn.com/t/2851331_ac75kuoita5.woff') format('woff'),
        url('//at.alicdn.com/t/2851331_ac75kuoita5.ttf') format('truetype');
}
  1. 在子应用中使用字体
[class^="el-icon-"], [class*=" el-icon-"] {
  font-family: "iconfont" !important;
}
shadow 中 css 3 变量作用域

将原本 :root { } 改成 :host { } 即可

© 2016 - 2021 BY 禾惠 粤ICP备20027042号