Skip to content

一、配置主应用

1. 创建一个vue3项目,在项目中安装qiankun

npm i qiankun -S

2. 注册微应用并启动,在main.ts中:

js
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import router from './router'
import { registerMicroApps, start } from 'qiankun';  
import type { ObjectType, RegistrableApp } from "qiankun";  

const app = createApp(App)

// 注册所有图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

const appConfig: RegistrableApp<ObjectType>[] = [{  
    name: 'vue-first-app', // 微应用的名称,需要唯一
    entry: '/app-vue-history/', // 微应用的入口,部署配置
    // entry: '//localhost:3001', // 微应用的入口,本地开发
    container: '#vue-first-app', // 主应用中用于渲染微应用的容器, 微应用渲染的节点容器
    activeRule: '/app-vue-history', // 微应用的激活规则,匹配路由,跟子应用路由要相同
},]  

registerMicroApps(appConfig)  
// 启动 qiankun
start({  
    prefetch: 'all',  
    sandbox: {  
        experimentalStyleIsolation: true,  
    }  
});  

app.use(createPinia())
app.use(router)
app.use(ElementPlus)

app.mount('#app')

3. 注册子应用路由

ts
import { createRouter, createWebHistory } from 'vue-router'
import AppView from '../views/AppView.vue'

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
        {
            path: '/',
            name: 'home',
            component: AppView,
        },
        // 微应用1相关路由 - 这些路由将会被qiankun接管,但需要在主应用中保留占位
        {  
            path: '/app-vue-history/:pathMatch(.*)*',// app-vue-history要跟activeRule的值一样,用于匹配路由,代表当前所有app-vue-history的路由,都访问子应用页面
            name: 'vue-history',  
            component: AppView,  
        },  
    ],
})

export default router

4. 增加子应用路由页面

在路由的component页面(AppView)增加配置

vue
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import TheWelcome from '../components/TheWelcome.vue'

const route = useRoute()

// 计算当前是否显示微应用
const isMicroApp = computed(() => {
    return route.path.startsWith('/app-vue-history')
})

// 计算当前微应用的ID
const microAppId = computed(() => {
    if (route.path.startsWith('/app-vue-history')) {
        return 'vue-first-app' // 对应注册子应用时的container
    }
    return ''
})
</script>

<template>
    <div class="app-view-container">
        <!-- 主应用内容 -->
        <main v-if="!isMicroApp" class="main-app-content">
            <TheWelcome />
        </main>
        <!-- 微应用容器 -->
        <div v-else class="micro-app-container">
            <!-- 微应用加载指示器 -->
            <div class="loading-indicator">
                <p>正在加载微应用...</p>
            </div>
            <!-- 微应用挂载点 -->
            <div :id="microAppId" class="micro-app-mount"></div>
        </div>
    </div>
</template>

5. 增加路由跳转

vue
<!-- 统一根据规则activeRule:app-vue-history,进行子应用路由匹配, home和about是子应用内部的路由 -->
<el-menu-item index="/app-vue-history/home">首页</el-menu-item>
<el-menu-item index="/app-vue-history/about">关于</el-menu-item>

二、配置子应用

1. qiankun目前不支持vite,安装插件vite-plugin-qiankun

npm install vite-plugin-qiankun

2. 修改vite.config.ts文件

ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import qiankun from "vite-plugin-qiankun"; // 配置乾坤插件

let path = process.env.NODE_ENV == "development" ? "/app-vue-history/" : "/app-vue-history/";
// https://vite.dev/config/
export default defineConfig({
    plugins: [
        vue(),
        vueDevTools(),
        qiankun("vue-first-app", { //和注册子应用时的name一致
            useDevMode: true, 
        }) 
    ],
    // 因为主应用activeRule:app-vue-history,开发模式下统一在次配置app-vue-history,就不用在路由中多次填写app-vue-history。
    // 部署情况,需要和entry一样。
    base: path, 
    server: {
        port: 3001, // 本地开发端口需要对应主应用注册的端口号
        cors: true, // 启用 CORS
        headers: {
        'Access-Control-Allow-Origin': '*', // 开发环境下允许所有来源
        }
    },
    resolve: {
        alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
        },
    },
})

3. 路由配置

ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      name: "app1Home",
      path: "/home", // 因为base填写了:app-vue-history,这就不用写了。
      component: HomeView,
    },
    {
      path: '/about',  
      name: 'app1About',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('../views/AboutView.vue'),
    },
  ],
})

export default router

4. 修改main.ts

ts
import './assets/main.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import { 
    renderWithQiankun,  
    qiankunWindow, 
    type QiankunProps, 
} from "vite-plugin-qiankun/dist/helper"; 

function render(props: QiankunProps = {}) { 
    const { container } = props; 
    const app = createApp(App); 
    app.use(createPinia()); 
    app.use(router); 
    container 
    // app1要对应html文件中的id
    ? app.mount(container.querySelector("#app1") as HTMLElement) 
    : app.mount("#app"); 
}

function initApp() { 
    if (!qiankunWindow.__POWERED_BY_QIANKUN__) { 
        console.log("%c 独立渲染", "color: red; font-size: 20px;"); 
        render(); 
        return; 
    } 
    renderWithQiankun({ 
        mount(props) { 
            console.log("%c qiankun 渲染", "color: red; font-size: 20px;"); 
            console.log(props); 
            render(props); 
        }, 
        bootstrap() { 
            console.log("bootstrap"); 
        }, 
        unmount(props) { 
            console.log("unmount", props); 
        }, 
        update(props) { 
            console.log("update", props); 
        }, 
    }); 
} 

initApp() 

三、部署到Nginx

Nginx配置:

conf
server {
        listen       80;
        server_name  app1.myapp.local; # 域名

        # 主应用
        location / {
            root   html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html; # 支持Vue路由的history模式
        }
        #子应用
        location /app-vue-history {
            alias  html/child/vue-history/; #通过alias替换 子应用构建后的dist目录路径
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;

             # CORS头部配置
            add_header Access-Control-Allow-Origin '*';
            add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
            add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
            add_header Cache-Control no-cache; # 强制刷新,要不子应用更新后,访问不刷新

            if ($request_method = 'OPTIONS') {
                return 204;
            }
        }
    }

Nginx目录

└── html/                     # 根文件夹
    |
    ├── child/                # 存放所有微应用的文件夹
    |   ├── vue-history/      # 存放微应用 vue-history 的文件夹
    ├── index.html            # 主应用的index.html
    ├── css/                  # 主应用的css文件夹
    ├── js/                   # 主应用的js文件夹

最后更新时间:

Released under the MIT License.