使用wujie搭建微前端应用及踩坑

1,875 阅读3分钟

线上演示地址:wujie-app

源码地址:github.com/Jiang-K-J/m… (如果觉您得有用,请帮忙点个小星星)

主应用:vue2+webpack

子应用:vue3+vite

子应用:react 18 + webpack

无界是微前端框架,有原生版(可在NPM下载),官方同时还针对Vue2、Vue3、React做了不同封装(也可在NPM下载)。封装的意义在于,原生配置较繁琐,而用封装好的版本(如wujie-vue2)操作更简单便捷,下面要讲解基于wujie-vue2封装好的框架开发,实际项目中也多用这种开发方式。

安装

主应用配置

  1. 准备

首先准备一个vue2的项目,可以是新项目,也可以是已经有过开发的项目,这都不影响wujie微前端框架的使用

  1. 安装
npm i wujie-vue2 -S
  1. 引入
// vue2
import WujieVue from "wujie-vue2";

const { bus, setupApp, preloadApp, destroyApp } = WujieVue;

Vue.use(WujieVue);
  1. 使用
<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>
  1. 属性介绍

更详细的介绍请参考:wujie可用属性的介绍与使用

子应用配置

无界对子应用的侵入非常小,在满足跨域条件下子应用可以不用改造。但是实际开发中,一个成熟的wujie子应用,我们一般需要对它的生命周期进行改造。注意,不同的子应用生命周期改造方式不同,可以参考官方文档,下面我们将讲解对vite构建的vue3子应用进行生命周期改造。

请在你的main.ts文件中加入下方代码

// 你的路由文件
const routes = [
  { path: '/', component: About },
  { path: '/about', component: About },
  { path: '/skip', component: Skip },
  { path: '/connect', component: Connect },
  { path: '/keepAlive', component: KeepAlive },
  { path: '/isolation', component: Isolation }
]

declare global {
  interface Window {
    // 是否存在无界
    __POWERED_BY_WUJIE__?: boolean;
    // 子应用mount函数
    __WUJIE_MOUNT: () => void;
    // 子应用unmount函数
    __WUJIE_UNMOUNT: () => void;
    // 子应用无界实例
    __WUJIE: { mount: () => void };
  }
}

if (window.__POWERED_BY_WUJIE__) {
  let instance: any;
  window.__WUJIE_MOUNT = () => {
    const router = createRouter({ history: createWebHistory(), routes });
    instance = createApp(App)
    instance.use(router);
    instance.mount("#app");
  };
  window.__WUJIE_UNMOUNT = () => {
    instance.unmount();
  };
  /*
    由于vite是异步加载,而无界可能采用fiber执行机制
    所以mount的调用时机无法确认,框架调用时可能vite
    还没有加载回来,这里采用主动调用防止用没有mount
    无界mount函数内置标记,不用担心重复mount
  */
  window.__WUJIE.mount()
} else {
  createApp(App).use(createRouter({ history: createWebHistory(), routes })).mount("#app");
}

基本使用

  1. Props传参
  • 主应用
<template>
  <WujieVue
    width="100%"
    height="100%"
    name="about-vue"
    :url="$v3Url"
    :props="{ username: 'JohnDoe', theme: 'dark' }"
  />
</template>

<script>
export default {
  data() {
    return {
      $v3Url: "https://subapp.example.com",
    };
  },
};
</script>
  • 子应用 :子应用可以通过 window.$wujie.props 获取主应用传递的参数。window.$wujie 这个属性,是wujie自动注入到子应用的windo上的,不需要你做任何操作
const props = window.$wujie?.props;
console.log(props.username); // 输出:JohnDoe
console.log(props.theme);    // 输出:dark
  1. 路由跳转

其实就是主应用通过Props传递一个给子应用,子应用触发主应用的函数实现路由跳转。当然,你也可以将整个路由对象传递给子应用,让子应用实现自定义跳转

  • 主应用
<template>
  <!-- 子应用 A -->
  <wujie-vue name="A" url="//hostA.com" :props="{jump}" ></WujieVue>
</template>

<script>
export default {
  methods: {
    jump(location) {
      this.$router.push(location);
    }
}
</script>
  • 子应用
// 子应用 A 点击跳转处理函数
function handleJump() {
  window.$wujie?.props.jump({ path: "/pathB" });
}
  1. 应用通信

Wujie 提供了 bus 作为事件总线,主应用和子应用都可以通过它来发送和接收事件。

  • 主应用 :主应用监听和发送事件
<template>
  <WujieVue
    width="100%"
    height="100%"
    name="about-vue"
    :url="$v3Url"
  />
  <button @click="sendMessageToChild">发送消息到子应用</button>
</template>

<script>
export default {
  methods: {
    sendMessageToChild() {
      window.$wujie?.bus?.$emit("message-from-main", {
        msg: "Hello from Main App",
      });
    },
  },
  mounted() {
    window.$wujie?.bus?.$on("message-from-child", (data) => {
      console.log("收到子应用消息:", data);
    });
  },
};
</script>
  • 子应用 :子应用监听和发送事件
if (window.$wujie?.bus) {
  // 监听主应用的消息
  window.$wujie.bus.$on("message-from-main", (data) => {
    console.log("收到主应用消息:", data);
  });

  // 发送消息到主应用
  window.$wujie.bus.$emit("message-from-child", {
    msg: "Hello from Child App",
  });
}

部署

wujie的部署和普通项目部署没有区别,只是子应用需要配置nginx来允许跨域访问

  • 主应用nginx配置
location / {
      index index.html;
      try_files $uri /index.html;
    }
  • 子应用nginx配置
location / {
      index index.html;
      try_files $uri /index.html;
      
       # 添加跨域头
      add_header Access-Control-Allow-Origin *;
      add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
      add_header Access-Control-Allow-Headers "Content-Type, Authorization";
  
      if ($request_method = OPTIONS) {
          add_header Access-Control-Allow-Origin *;
          add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
          add_header Access-Control-Allow-Headers "Content-Type, Authorization";
          return 204;
      }
    }

踩坑总结

  1. UI组件库样式丢失——增加patchElementHook插件
 <WujieVue
    width="100%"
    height="100%"
    name="xxx"
    :url
    :props="{ jump }"
    :plugins="[{
      patchElementHook(element, iframeWindow) {
        if (element.nodeName === "STYLE") {
          element.insertAdjacentElement = function (_position, ele) {
            iframeWindow.document.head.appendChild(ele);
          };
        }
      },
    }]"
  ></WujieVue>
  1. el-select 位置偏移以及 el-table tootip 位置偏移的问题

持续更新中****