# vue3 + vite + qiankun
**Repository Path**: ABCSDSD_123456/vue3-vite-qiankun
## Basic Information
- **Project Name**: vue3 + vite + qiankun
- **Description**: vue3 + vite + qiankun 实现主应用,接入 react18 和 vue3 微应用
- **Primary Language**: TypeScript
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 5
- **Forks**: 1
- **Created**: 2024-07-09
- **Last Updated**: 2025-02-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# vue3 + vite + qiankun
#### 介绍
vue3 + vite + qiankun 或 react18 + vite + qiankun 或 react18 + vite + qiankun 实现主应用,接入 react18 和 vue3 微应用
# 具体实现
# 以 vue3+vite+qiankun 为例实现微前端架构
## 项目源码地址:
> 项目源码地址:https://gitee.com/ABCSDSD_123456/vue3-vite-qiankun
## 项目初始化
### 第一步 新建项目文件夹 /qiankun-micro-frontEnd

### 第二步 初始化项目生成 package.json 文件
```cmd
# 生成 package.json 文件
npm init
```
```json
// package.json 文件配置如下
{
"name": "package.json",
"version": "1.0.0",
"description": "使用 vue3 为主应用,react18 和 vue3 为微应用的 qiankun 微前端实现",
"scripts": {
"dev": "cd main-app && vite",
"dev:sub-app-vue3": "cd sub-app-vue3 && vite",
"dev:sub-app-react18": "cd sub-app-react18 && vite",
"serve": "cd main-app && vite",
"serve:sub-app-vue3": "cd sub-app-vue3 && vite",
"serve:sub-app-react18": "cd sub-app-react18 && vite",
"build": "cd main-app && vite build",
"build:sub-app-vue3": "cd sub-app-vue3 && vite build",
"build:sub-app-react18": "cd sub-app-react18 && vite build",
"preview": "cd main-app && preview",
"preview:sub-app-vue3": "cd sub-app-vue3 && preview",
"preview:sub-app-react18": "cd sub-app-react18 && preview"
},
"author": "jianhaijiyusheng",
"license": "MIT"
}
```
## 主应用配置
### 第一步 初始化一个 vue3 + vite 项目并在项目中安装 qiankun
> vue3 + vite 项目框架代码示例:[vue-project-framework(gitee.com)](https://gitee.com/ABCSDSD_123456/vue-project-framework)
```cmd
npm i qiankun
# 当前示例所使用的 qiankun 版本为 2.10.16
```
### 第二步 在 src/utils 下新建一个 micro-app.ts 文件用于存放微前端配置
```ts
// micro-app.ts 文件内容如下:
const microApps = [{
name: 'subVueAPP', // 子应用的唯一 id
entry: '//localhost:3001', // 子应用的访问链接
activeRule: '/vue-app', // 当访问的 url 中匹配到 activeRule 中的参数,就会到跳转到对应的子应用
}, {
name: 'subReactAPP',
entry: '//localhost:3002',
activeRule: '/react-app'
}];
const apps = microApps.map(item => {
return {
...item,
container: "#sub-app", // 用于声明 子应用 应该挂载到 主应用 中的哪个 DOM 下
};
});
export default apps;
```
### 第三步 配置主应用中的 main.ts
```ts
import { createApp } from 'vue'
import './style.scss'
import App from './App.vue'
import router from './router/index'
import { createPinia } from 'pinia'
import { Http } from '@/utils/http'
import components from '@/components/index'
import i18n from '@/i18n/index'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/display.css'
import ElementPlus from 'element-plus'
import directive from '@/directive'
import '@/plugin/last-commit-msg/toggle.js'
import { mockRuqest } from './mock'
import { registerMicroApps, start } from 'qiankun'
import microApp from './utils/micro-app'
import '@/utils/qiankun-action'
Http.init()
if (import.meta.env.MODE === 'mock') {
mockRuqest()
}
const store = createPinia()
const app = createApp(App)
// ---- qiankun 主要配置 -------
// 配置微应用所需参数
registerMicroApps(microApp)
// 使用 qiankun 启动项目
start()
app.use(ElementPlus)
app.use(store)
app.use(router)
app.use(components)
app.use(i18n)
app.use(directive)
app.mount('#app')
```
### 第四步 增加子应用启动页面 micro-app.vue
```vue
// src/micro-app/micro-app.vue 文件内容如下
子应用
```
### 第四步 配置主应用路由
```ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Layout from '@/page/layout/layout.vue'
const routes: Array = [{
path: '/',
redirect: '/index',
}, {
path: '/',
name: 'layout',
component: Layout,
children: [{
path: 'index',
name: 'index',
component: () => import('@/page/main-app/index/index.vue'),
}, {
path: 'about',
name: 'about',
component: () => import('@/page/main-app/about/about.vue'),
}, {
path: 'setting',
name: 'setting',
component: () => import('@/page/main-app/setting/setting.vue'),
}, {
path: 'user-info',
name: 'userInfo',
component: () => import('@/page/main-app/user-info/user-info.vue'),
}, {
// vue 子应用路由
path: 'vue-app/index',
name: 'vueApp',
component: () => import('@/page/micro-app/micro-app.vue'), // 子应用中转配置页面,用于启动子应用
}, {
// react 子应用路由
path: 'react-app/index',
name: 'reactApp',
component: () => import('@/page/micro-app/micro-app.vue'), // 子应用中转配置页面,用于启动子应用
}]
}]
const router = createRouter({
history: createWebHistory(),
routes,
})
router.beforeEach((to, from, next) => {
next()
})
export default router
```
### 第五步 主应用路由跳转逻辑
> 跳转子应用可以使用路由方式跳转,也可以使用 window.history.pushState 跳转
>
> 1、state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。
>
> 2、title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。
>
> 3、url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
>
> window.history.pushState(null, '', '/vue-app/index')
```vue
```
## 子应用配置(vue3 + vite)
### 第一步 安装插件
```cmd
npm i vite-plugin-qiankun
```
### 第二步 配置 vite.config.ts
```ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
import qiankun from 'vite-plugin-qiankun'
export default defineConfig({
base: '/vue-app/',
plugins: [
// ...此处省略其他插件
// qiankun(name, options) name 的取值需要和 主应用 @/utils/micro-app.ts 配置的 name 保持一致
qiankun('subVueAPP', { useDevMode: true })
],
build: {
outDir: 'dist/sub-app-vue3'
}
})
```
### 第三步 配置 main.ts
```ts
import { createApp } from 'vue'
import './style.scss'
import App from './App.vue'
import router from './router/index'
import { createPinia } from 'pinia'
import { Http } from '@/utils/http'
import components from '@/components/index'
import i18n from '@/i18n/index'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/display.css'
import ElementPlus from 'element-plus'
import directive from '@/directive'
import { renderWithQiankun, qiankunWindow } from "vite-plugin-qiankun/dist/helper"
Http.init()
let app: any = null
const render = (props?: any) => {
const { container } = props ?? {}
const store = createPinia()
app = createApp(App)
app.use(ElementPlus)
app.use(store)
app.use(router)
app.use(components)
app.use(i18n)
app.use(directive)
app?.mount(container ? container.querySelector('#container') : '#container');
}
renderWithQiankun({
async mount(props: any): Promise {
// await props.onGlobalStateChange((state: any) => {
// console.log("子应用接收的参数", state);
// state.publicPath && window.localStorage.setItem("mainJumpPublicPath", state.publicPath);
// }, true);
return new Promise((resolve, reject) => {
resolve('mount');
})
},
bootstrap(): Promise {
console.log("%c", "color:green;", " ChildOne bootstrap");
return new Promise((resolve, reject) => {
resolve('bootstrap')
})
},
update() {
console.log("%c", "color:green;", " ChildOne update");
},
unmount(props: any) {
console.log("sub app vue3 unmount", props);
app.unmount();
app._container.innerHTML = "";
app = null;
// 如果 子应用 是直接挂载到 主应用的 #app DOM 下,
// 那从 子应用 通过浏览器回退按钮回退到 主应用 时会出现白屏,刷新后又正常显示
// 这是因为通过浏览器回退按钮回退到 主应用 时主应用的 main.ts 文件不会重新执行,所以 DOM 被重新加载
// 可以在子应用卸载后刷新页面
// window.location.reload()
}
});
// 判断是否是 qiankun 渲染
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
render()
}
```
## 子应用配置(react18)
### 第一步 配置基本
> 使用的插件和vite.config.ts 文件中的配置同上述 vue3
>
> 但是如果在 vite.config.ts 中使用 @vitejs/plugin-react 会如下错误,在 qiankun 中运行时需要删除
```
[import-html-entry]: error occurs while executing normal script
```
### 第二步 配置main.tsx 文件
```tsx
import './public-path.js'
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './style.less';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { ConfigProvider } from 'antd';
import store from '@/store';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
const render = (props: any = {}) => {
const { container } = props;
ReactDOM.createRoot(container ? container.querySelector('#root')! : document.getElementById('root')!).render(
);
};
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
render({});
}
renderWithQiankun({
async mount(props: any): Promise {
render(props);
// await props.onGlobalStateChange((state: any) => {
// // console.log('子应用接收的参数', state);
// });
return new Promise((resolve, reject) => {
resolve('mount')
})
},
bootstrap(): Promise {
console.log('%c', 'color:green;', ' ChildOne bootstrap');
return new Promise((resolve, reject) => {
resolve('bootstrap');
});
},
update() {
console.log('%c', 'color:green;', ' ChildOne update');
},
unmount(props: any) {
console.log('sub app vue3 unmount', props);
// const { container } = props;
// ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.querySelector('#root'));
// window.location.reload()
},
});
```