前言
- 微前端框架:无界 wujievue 微前端是什么 | 无界
- 主应用:Vue 2 + elementui
- 子应用:Vue 3+vite+element plus
前提
- 子应用的资源和接口的请求都在主域名发起,所以会有跨域问题,子应用必须做cors 设置
- vue3+vite 项目跨域配置参考
// vite.config.js
export default defineConfig({
// 其它配置...
server: {
proxy: {
'^/api': {
target: '协议地址端口', //目标源,目标服务器,真实请求地址
changeOrigin: true, //支持跨域
rewrite: (path) => path.replace(/^\/api/, "/api"), //重写真实路径,替换/api
}
}
}
})
快速上手
S1. 安装 wujievue
# vue2 框架
npm i wujie-vue2 -S
# vue3 框架
npm i wujie-vue3 -S
S2 . 主应用设置
main.js
引入 wujiwvue
import WujieVue from "wujie-vue2";
Vue.use(WujieVue);
- 创建一个 vue组件并使用 wujievue
<WujieVue
width="100%"
height="100%"
name="xxx"
:url="xxx"
:sync="true"
:fetch="fetch"
:props="props"
:beforeLoad="beforeLoad"
:beforeMount="beforeMount"
:afterMount="afterMount"
:beforeUnmount="beforeUnmount"
:afterUnmount="afterUnmount"
></WujieVue>
- wujievue Props 说明 setupApp | 无界
{
/** 唯一性用户必须保证 */
name: string;
/** 需要渲染的url */
url: string;
/** 需要渲染的html, 如果用户已有则无需从url请求 */
html?: string;
/** 代码替换钩子 */
replace?: (code: string) => string;
/** 自定义fetch */
fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
/** 注入给子应用的属性 */
props?: { [key: string]: any };
/** 自定义运行iframe的属性 */
attrs?: { [key: string]: any };
/** 自定义降级渲染iframe的属性 */
degradeAttrs?: { [key: string]: any };
/** 子应用采用fiber模式执行 */
fiber?: boolean;
/** 子应用保活,state不会丢失 */
alive?: boolean;
/** 子应用采用降级iframe方案 */
degrade?: boolean;
/** 子应用插件 */
plugins?: Array<plugin>;
/** 子应用生命周期 */
beforeLoad?: lifecycle;
beforeMount?: lifecycle;
afterMount?: lifecycle;
beforeUnmount?: lifecycle;
afterUnmount?: lifecycle;
activated?: lifecycle;
deactivated?: lifecycle;
loadError?: loadErrorHandler;
};
S3 子应用设置
- main. js 改造
将原项目中的
const app = createApp (App);
app.use (pinia). use (router);
app.mount (" #app ");
改造为如下格式
if (window.__POWERED_BY_WUJIE__) {
let app;
window.__WUJIE_MOUNT = () => {
app = createApp(App).use(router).use(pinia);
app.mount("#app");
};
window.__WUJIE_UNMOUNT = () => {
app.unmount();
};
// module脚本异步加载,应用主动调用生命周期
window.__WUJIE.mount();
} else {
const app = createApp(App);
app.use(pinia).use(router);
app.mount("#app");
}
数据通信
props 通信
- 主应用可以通过 props 注入数据和方法:
<WujieVue name="xxx" url="xxx" :props="{ data: xxx, methods: xxx }"></WujieVue>
- 子应用可以通过 $wujie 来获取:
const props = window.$wujie?.props; // {data: xxx, methods: xxx}
windows 通信
由于子应用运行的
iframe
的src
和主应用是同域的,所以相互可以直接通信
- 主应用调用子应用的全局数据
window.document.querySelector("iframe[name=子应用id]").contentWindow.xxx;
- 子应用调用主应用的全局数据
window.parent.xxx;
eventBus 通信
无界提供一套去中心化的通信方案,主应用和子应用、子应用和子应用都可以通过这种方式方便的进行通信, 详见 api
- 主应用使用方式:
// 如果使用wujie
import { bus } from "wujie";
// 如果使用wujie-vue
import WujieVue from "wujie-vue";
const { bus } = WujieVue;
// 如果使用wujie-react
import WujieReact from "wujie-react";
const { bus } = WujieReact;
// 主应用监听事件
bus.$on("事件名字", function (arg1, arg2, ...) {});
// 主应用发送事件
bus.$emit("事件名字", arg1, arg2, ...);
// 主应用取消事件监听
bus.$off("事件名字", function (arg1, arg2, ...) {});
- 子应用使用方式:
// 子应用监听事件
window.$wujie?.bus.$on("事件名字", function (arg1, arg2, ...) {});
// 子应用发送事件
window.$wujie?.bus.$emit("事件名字", arg1, arg2, ...);
// 子应用取消事件监听
window.$wujie?.bus.$off("事件名字", function (arg1, arg2, ...) {});
疑难杂症
问题1: 子应用中 Element plus 冒泡系列组件(比如下拉框 Select)弹出位置不正确
解决方案: 将子应用将 body
设置为 position: relative
即可
问题2: 子应用点击事件频繁报错: [Vue warn]: Invalid event arguments: event validation failed for event "click"
解决方案: 使用 wujie-polyfill 提供的插件解决 Site Unreachable
- 安装 wujie-polyfill
npm i wujie-polyfill -S
- 给无界
<WujieVue>
组件添加 plugins 属性
<WujieVue
width="100%"
height="100%"
name="xxx"
:url="xxx"
:plugins=“[InstanceofPlugin()]”
></WujieVue>
// script 中引入
import { InstanceofPlugin } from "wujie-polyfill";
问题3: 子应用中滚动事件失效,导致 sticky 样式失效,el-backtop 等组件失效
解决方案: 为子应用中的根元素设置样式 style="position: relative; height: calc(100vh - 120px); overflow-y: scroll"