前言
- 微前端框架:无界 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
<WujieVuewidth="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 属性
<WujieVuewidth="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"