最近项目需要使用到微前端,在经过对乾坤和无界的初步调研后,决定用无界。主应用使用webpack(5.85.1)和vue2(2.6.11),子应用使用vite(4.3.5)和vue3(3.2.47)。本文主要记录一下在使用无界微前端过程中遇到的一些问题以及解决办法。
1.子应用使用到百度地图以及报错
解决子应用使用百度地图报错问题需要使用如下插件:
js
const plugins = [
{
jsIgnores: [/baidu/, /amap.com/], // [/amap.com/]
// _element为真正插入的元素, _iframeWindow为子应用的window,rawElement为原始插入元素
appendOrInsertElementHook(_element, _iframeWindow, rawElement) {
// 以下函数也可以用此行代替 _iframeWindow.document.body.removeChild=() => null
if (rawElement) {
Object.defineProperty(rawElement, "parentNode", {
get: () => ({ removeChild: () => null }),
});
}
}
}
];
export default plugins;
jsIgnores: 如果用户想子应用自己加载某些js文件(通过script标签),而非框架劫持加载,那么这些工作可以放置在js-ignores中进行。比如子应用使用到了百度或者高德的地图,要自己加载。
appendOrInsertElementHook:子应用往body、head插入元素后执行的回调函数。笔者因为使用到了百度地图,出现了removeChild报错问题,所以使用这个插件来解决问题。详见文章:无界微前端作为子应用引入高德或百度地图removeChild报错问题解决。
2.子应用组件样式丢失
解决子应用样式丢失的问题需要使用如下插件:
js
patchElementHook(element, iframeWindow) {
if (element.nodeName === "STYLE") {
element.insertAdjacentElement = function (_position, ele) {
iframeWindow.document.head.appendChild(ele);
};
}
},
},
patchElementHook:子应用创建元素后执行的回调函数,详见issue:vite4子应用切换 样式丢失 。
3.子应用请求跨域设置
由于子应用的资源和接口的请求都是在主域名发起的,所以会有跨域问题,子应用必须做cors设置。然而无界的指南中给的参考并不适用于笔者的项目。解决跨域的方法是配置子应用的ngnix:
ngnix
server {
listen 80;
location / {
root /app/dist/;
index index.html;
try_files $uri $uri/ /index.html;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header 'Access-Control-Allow-Origin' *;
}
}
关于子应用跨域问题可以查看issue: 子应用xhr请求跨域问题。跨域的设置可以查看:k8s跨域解决。同一环境只能调用同一环境的域名。
4.隐藏子应用左侧的菜单栏、改变主题色
业务要求当子应用单独登录时则显示子应用顶部的菜单栏和子应用自己默认的主题色,但是通过微前端系统登录时则隐藏顶部的菜单栏并显示和主应用相同的主题色。具体处理过程如下:
(1)main.ts 检查是否是通过主应用启动的
js
if ((window as any).__POWERED_BY_WUJIE__) {
setTimeout(() => {
console.log("子应用通过无界启动");
const globalStore = useGlobalStore();
globalStore.$patch({
header: false
});
// globalStore.header = false;
console.log("globalStore", globalStore);
// pinia.state.value["Lio-global"].header = false;
// console.log(pinia.state.value["Lio-global"]);
const { changePrimary } = useTheme();
changePrimary("#194ed9");
});
}
(2)store下的global.ts的state增加是否显示header的配置项:
js
state: (): GlobalState => ({
// 当前页面是否全屏
maximize: false,
// 主题颜色
primary: DEFAULT_PRIMARY,
// 页脚
footer: false,
header: true
}),
(3)LayoutVertical\index.vue增加对UI层的控制:
模板部分:
html
<el-container>
<el-header v-if="showHeader">
<ToolBarLeft />
<ToolBarRight />
</el-header>
<Main />
</el-container>
script部分:
js
import { computed } from "vue";
import { useGlobalStore } from "@/stores/modules/global";
const globalStore = useGlobalStore();
const showHeader = computed(() => globalStore.header);
5.element-plus的table组件tooltip出问题
子应用发布后发现table组件中tooltip组件的显示位置有问题,如下图所示:
在github上看了相关的issues试了一些解决方法发现解决不了问题, 于是笔者提了一个issue后,另辟蹊径解决问题即增加自定义的tooltip:
(1)template代码:主要是使用title属性和slot插槽
html
<template>
<div class="custom-tooltip" :title="typeof props.title === 'string' ? props.title : hanleVnode(props.title, '')">
<slot></slot>
</div>
</template>
(2)script代码:递归处理vnode节点以及拼接处理结果
js
import { isVNode } from "vue";
const hanleVnode = (vnode: any, res = "") => {
const flag = isVNode(vnode);
if (flag) {
const target: any = vnode.children;
if (typeof target === "string") {
res += target;
return res;
} else if (Array.isArray(target)) {
for (let i = 0; i < target.length; i++) {
if (isVNode(target[i])) {
res += hanleVnode(target[i]);
} else if (typeof target[i] === "string") {
res += target[i];
}
}
}
return res;
} else {
return "";
}
};
(3)接下来看对组件的使用
原来的代码如下:
修改方法也很简单,只需要增加死否通过主应用启动的,如果是则使用Tootip组件:
6.登录登出
在笔者开发的微前端平台中,业务要求是主应用登录后,子应用可以登录;子应用也可以单独登录,如果主应用登录后则将token传递给子应用,子应用调用接口时将通过header传递接口。
解决办法是主应用通过props将token传递给子应用:
js
<WujieVue
width="100%"
height="100%"
name="vue3-1"
:url="vue3Url"
:alive="true"
:props="{ jump, token }"
></WujieVue>
子应用axios请求拦截器:
js
if (config.headers && typeof config.headers.set === "function") {
if ((window as any).__POWERED_BY_WUJIE__) {
console.log("子应用通过无界启动");
const props: any = (window as any).$wujie?.props;
config.headers.set("Authorization", props.token);
console.log(props);
} else {
config.headers.set("Authorization", userStore.token);
}
}
子应用登录失效或者token过期:
js
if ((window as any).__POWERED_BY_WUJIE__) {
console.log("子应用通过无界启动");
// 通知主应用重新登录
const props: any = (window as any).$wujie?.props;
console.log(props);
props.jump();
} else {
userStore.setToken("");
router.replace(LOGIN_URL);
ElMessage.error("登录失效,请重新登录");
tryHideFullScreenLoading();
return Promise.reject(response);
}
7.子应用图片显示异常
子应用原来引入图片的方式为:
js
const requireImg = (imgPath: string) => {
return new URL(`../../assets/images/modelImg/${imgPath}`, import.meta.url).href;
};
也就是通过new URL的方式,但是会导致子应用图片的绝对地址被替换成为了主应用的host。笔者的问题已经修复,就引一张网友的截图吧:
这样会导致子应用中的图裂掉。在无界的文档中,给出了子应用的相对图片地址没有替换成绝对地址的解决办法,但是不适用本问题。
于是笔者检索了一下github上的issues, 发现有网友遇到同样的问题:new URL加载图片404 ,此issue状态为open。所以笔者尝试使用直接引入如片的方式:
js
import img001 from "@/assets/images/modelImg/10001.png";
重新打包上线,发现问题得到解决。
总结
大家在使用无界微前端过程中都会遇到什么问题呢?可能会遇到和笔者相同的问题,可能是不一样的问题。回过头来看我们是如何解决这些问题呢?一般都是要么搜索引擎搜索一波,要么是通过查阅官方文档,再或者通过查看github上的issues。网上给的答案有能直接用的,有的是能够给我们带来启发的。有的问题解决起来比较轻松,有的问题解决起来比较棘手,但是不管咋地,解决问题就是一个学习和进步的过程。看到github上那些大佬的留言以及解决问题的方法真是收益匪浅。
参考资料
【1】无界官方文档
【2】无界github地址