记录使用 qiankun 遇到的一些问题
老项目使用的是 Vue 2 + ElementUI ,而且项目中又集成很多个子系统。考虑到项目越来越庞大,构建时间也越来越长,再加上技术上难于升级,所以引入了 qiankun 的解决方案。
大致思路是主应用使用 Vue 3 + ElementPlus ,然后将老项目直接作为一个子应用,后期再从老项目中将子系统一个一个的拆出作为子应用。
qiankun 的 API 不多,看起来不难,但在使用中也遇到了不少的问题,本文记录了目前遇到的一些问题。
应用间的样式隔离
不使用 qiankun 的样式隔离方案( strictStyleIsolation 和 experimentalStyleIsolation )。
不使用的原因
- 主应用使用了 ElementPlus ,子应用使用了 ElementUI ,由于两个 UI 框架使用的 className 大都一样,如果使用 strictStyleIsolation 方案会导致主应用污染子应用的样式
- 如果使用 experimentalStyleIsolation 会导致子应用无法启动(应该是 Vue 不能找到挂载节点)
解决的思路
- 创建一个 ShadowDom( ShadowDom 挂载在页面中的某个元素上),再通过
document.createElement('div')
创建 containerDiv 和 appDiv 两个 DIV DOM - containerDiv 作为 qiankun 的容器(对应 qiankun 的
container
参数),主要作用是通过 qiankun 把需要的资源加载进来 - appDiv 用于 Vue 挂载节点
- 将 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
是不生效的。
解决的思路
- 在主应用中加载字体
@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');
}
- 在子应用中使用字体
[class^="el-icon-"], [class*=" el-icon-"] {
font-family: "iconfont" !important;
}
shadow 中 css 3 变量作用域
将原本 :root { }
改成 :host { }
即可