Vue3+vite与Qiankun实现的微前端Demo

关于Qiankun

qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

官方文档: https://qiankun.umijs.org/zh/guide

操作步骤

创建主应用

1pnpm create vue
2Project name: main
3cd main
4pnpm add qiankun -S
5pnpm install
6pnpm dev

main.ts

 1import { createApp } from 'vue'
 2import App from './App.vue'
 3import router from './router'
 4
 5
 6import { registerMicroApps, start } from "qiankun";
 7
 8const app = createApp(App)
 9
10const apps: any[] = [
11  {
12    name: "app1", // 应用的名字
13    entry: "http://localhost:9001/", // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch)
14    container: "#app1", // 要渲染到的节点id,对应上一步中src/App.vue中的渲染节点
15    activeRule: "/apps/app1"
16  },
17  {
18    name: "app2",
19    entry: "http://localhost:9002/",
20    container: "#app2",
21    activeRule: "/apps/app2"
22  },
23];
24registerMicroApps(apps); // 注册应用
25start({
26  prefetch:'all', // 预加载
27  sandbox: {
28      //experimentalStyleIsolation: true, //   开启沙箱模式,实验性方案
29  },
30}); // 开启应用
31
32app.use(router)
33app.mount('#app')

App.vue

 1      <nav>
 2        <RouterLink to="/">Home</RouterLink>
 3        <RouterLink to="/about">About</RouterLink>
 4        <!-- 新增app1路由 -->
 5        <router-link to="/apps/app1">app1</router-link>
 6        <!-- 新增app1路由 -->
 7        <router-link to="/apps/app1/about">app1/about</router-link>
 8        <!-- 新增app2路由 -->
 9        <router-link to="/apps/app2">app2</router-link>
10      </nav>
11  <RouterView />
12  <!-- 新增site1渲染节点 -->
13  <div id="app1" />
14  <!-- 新增app2渲染节点 -->
15  <div id="app2" />

创建app1

1pnpm create vue
2Project name: app1
3cd app1
4pnpm add vite-plugin-qiankun -S
5pnpm install
6pnpm dev

vite.config.ts

 1import { fileURLToPath, URL } from 'node:url'
 2
 3import { defineConfig } from 'vite'
 4import vue from '@vitejs/plugin-vue'
 5import qiankun from 'vite-plugin-qiankun'
 6
 7// https://vitejs.dev/config/
 8export default defineConfig({
 9  server: {
10    watch: { usePolling: true },
11    hmr: true,
12    port: 9001,
13    headers: {
14      'Access-Control-Allow-Origin': '*', // 主应用获取子应用时跨域响应头
15    },
16  },
17  plugins: [
18    vue(),
19    qiankun('vue3', {
20      useDevMode: true
21    })
22  ],
23  resolve: {
24    alias: {
25      '@': fileURLToPath(new URL('./src', import.meta.url))
26    }
27  }
28})

main.ts

 1import { createApp } from "vue";
 2import router from "./router";
 3import App from "./App.vue";
 4import {
 5  renderWithQiankun,
 6  qiankunWindow,
 7  QiankunProps,
 8} from "vite-plugin-qiankun/dist/helper";
 9const initQianKun = () => {
10  renderWithQiankun({
11    // bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap
12    // 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等
13    bootstrap() {
14      console.log("bootstrap app1");
15    },
16    // 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法,也可以接受主应用传来的参数
17    mount(_props: any) {
18      console.log("mount", _props);
19      render(_props.container);
20    },
21    // 应用每次 切出/卸载 会调用的unmount方法,通常在这里我们会卸载微应用的应用实例
22    unmount(_props: any) {
23      console.log("unmount", _props);
24    },
25    update: function (props: QiankunProps): void | Promise<void> {
26      console.log("update");
27    },
28  });
29};
30
31const render = (container) => {
32  // 如果是在主应用的环境下就挂载主应用的节点,否则挂载到本地
33  const appDom = container ? container : "#app";
34  const app = createApp(App);
35  app.use(router);
36  app.mount(appDom);
37};
38// 判断是否为乾坤环境,否则会报错iqiankun]: Target container with #subAppContainerVue3 not existed while subAppVue3 mounting!
39qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render(null);

router/index.ts

 1
 2import { createRouter, createWebHistory } from 'vue-router'
 3import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
 4const router = createRouter({
 5  history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? "/apps/app1" : "/"),
 6  routes: [
 7    {
 8      path: '/',
 9      name: 'home',
10      component: () => import('../views/Home.vue')
11    },
12    {
13      path: '/about',
14      name: 'about',
15      component: () => import('../views/About.vue')
16    },
17    {
18      path: '/:catchAll(.*)',
19      name: "404",
20      component: () => import('../App.vue')//这个是我自己的路径
21    },
22  ]
23})
24
25export default router

创建app2

与app1创建方式相同,只是名称不同

启动

分别在main/app1/app2目录下执行pnpm dev,浏览器访问 http://localhost:5173/ 即可

踩坑点

  • 由于应用的路由不在router/index.ts里,请求应用路由时会出现[Vue Router warn]: No match found for location with path的提示,在路由表中加/:catchAll(.*)可解决该类问题
  • initQianKun.mount 调用render时,要有完整的createApp().mount(),当你切换应用时,这个逻辑会重新调用。
  • 子应用的全局样式,会影响主应用,所以设计的时候要做到样式隔离

项目地址

https://github.com/vue-qiankun/vue3-vite-qiankun-demo

发布日期:2023-08-02 20:14 字数:528 用时 3分钟