# 微前端框架UmiJS
**Repository Path**: fang-yanwen/micro-front-end-framework
## Basic Information
- **Project Name**: 微前端框架UmiJS
- **Description**: 主应用技术框架:UmiJS+qiankun+vue3+ts
子应用技术框架:vue3+vite+ts
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 2
- **Created**: 2024-01-07
- **Last Updated**: 2025-11-30
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 微前端框架
#### 介绍
主应用技术框架:UmiJS+qiankun+vue3+ts
子应用技术框架:vue3+vite+ts
#### 安装教程
1. 使用pnpm i 下载主应用依赖
2. cd进入sonApp子应用文件夹将两个子应用使用pnpm i 下载依赖并使用pnpm dev启动子应用
3. 回到主应用使用 pnpm dev启动主应用,打开网页就可以了
# 如何使用UmiJS框架搭一个vue的微前端框架,技术栈使用:qiankun,pnpm
### UmiJS:[快速上手 (umijs.org)](https://umijs.org/docs/guides/getting-started)
### qiankun:[介绍 - qiankun (umijs.org)](https://qiankun.umijs.org/zh/guide)
#### 博客:[qiankun - 微前端应用搭建_react qiankun-CSDN博客](https://blog.csdn.net/z291493823/article/details/125891986)
#### 教程博客:[微前端(qiankun)使用手册 - 简书 (jianshu.com)](https://www.jianshu.com/p/be4183f6ae68)
## 注意:子应用图片加载不出来,就将图片放到src/assets文件夹下
| option | description |
| -------------- | -------------------------- |
| `--no-git` | 创建项目,但不初始化 Git |
| `--no-install` | 创建项目,但不自动安装依赖 |
## 使用 UmiJS 搭建一个 Vue 微前端框架并集成 qiankun 微前端框架的步骤如下:
### 1、安装 pnpm:首先确保你已经安装了 pnpm,可以通过运行 `npm install -g pnpm` 来安装。
### 2、创建一个 UmiJS 项目:运行以下命令来创建一个新的 UmiJS 项目:
```
pnpm dlx create-umi@4.1.0 --no-install
```
```bash
pnpm dlx create-umi@latest --no-install
```
#### 在创建项目的过程中,你可以选择使用 TypeScript 或者 JavaScript 作为项目的开发语言。
### 3、安装 qiankun:在项目根目录下运行以下命令来安装 qiankun:
```bash
pnpm i qiankun
```
### 4、创建config文件夹以及文件夹下的appData.ts和index.ts文件
#### appData.ts文件
```tsx
export const appArr = [
{
name: 'app1',
entry: 'http://localhost:8382/',
container: '#app1',
activeRule: '/app1',
}
]
//这里的 `your-subapp-name` 是你子应用的名称,
//`//localhost:8001` 是子应用的入口地址,
//`#your-subapp-container` 是子应用的容器选择器,
//`/your-subapp` 是子应用的激活规则。
```
#### index.ts
```tsx
import { registerMicroApps, addGlobalUncaughtErrorHandler, start, MicroAppStateActions, initGlobalState } from 'qiankun'
import { appArr } from './appData'
console.log("qiankun启动了");
registerMicroApps(appArr, {
// 加载前
// @ts-ignore
beforeLoad: (app) => console.log('加载前', app.name),
// 挂载前
// @ts-ignore
beforeMount: (app) => console.log('挂载前', app.name),
// 挂载后
// @ts-ignore
afterMount: (app) => console.log('挂载后', app.name),
// 卸载前
// @ts-ignore
beforeUnmount: (app) => console.log('卸载前', app.name),
// 卸载后
// @ts-ignore
afterMount: (app) => console.log('卸载后', app.name)
})
//启动 qiankun
start({
prefetch: 'all', //预加载
sandbox: {
experimentalStyleIsolation: true //开启沙箱模式
}
})
// 添加全局的未捕获异常处理器
addGlobalUncaughtErrorHandler((event) => {
// @ts-ignore
const { message } = event
if (message && message.includes('died in status LOADING_SOURCE_CODE')) {
console.error('微应用加载失败', event)
}
})
// 初始化 state
const state = {
str: '你好'
}
const actions: MicroAppStateActions = initGlobalState(state);
// 在当前应用监听全局状态,有变更触发回调函数
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log("主应用监听全局状态", state, prev);
});
// 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
actions.setGlobalState(state);
// 移除当前应用的状态监听,微应用 umount 时会默认调用
actions.offGlobalStateChange();
```
## 5、将config文件夹下的index.ts文件在全局vue文件(src/layouts/index.vue)中引入
## 6、根据appData中子应用的container属性值创建子应用容器,这里我创建了对应名字的页面,这里是因为UmiJS创建的应用会根据创建的vue文件名自动添加路由地址,路由文件在 src/.umi/core/route.tsx
### 在pages文件夹下创建appData文件的子应用的activeRule的名字文件 app1.vue
### app1.vue
```vue
```
### src/.umi/core/route.ts自动添加的路由
## 7、在全局文件src/layouts/index.vue中添加导航跳转,这里的路由要和src/.umi/core/route.ts自动添加的名字一样,也要和appData文件的子应用的activeRule的名字一样才能打开子应用
# 子应用需要抛出qiankun需要的生命周期函数,否则会加载子应用失败,vue2、vue3+vite 、react各有不同
#### 教程博客:[微前端(qiankun)使用手册 - 简书 (jianshu.com)](https://www.jianshu.com/p/be4183f6ae68)
### 1.1、Vue2 + Webpack 子应用
#### 动态配置资源路径
1. 在 `src` 目录新增 `public-path.js`:
```js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
1. 在应用入口 `index.js` 顶部导入 `public-path.js`。
```js
import "./public-path";
```
#### 公开生命周期函数
```js
import "./public-path";
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
let app: Vue | null;
function renderApp(container: string | HTMLElement) {
app = new Vue({
router,
store,
render: (h) => h(App),
}).$mount(container);
}
if (!window.__POWERED_BY_QIANKUN__) {
renderApp("#app");
}
export async function bootstrap() {
console.log("vue2 app bootstrap");
}
export async function mount(props: any) {
renderApp(props.container.querySelector("#app"));
}
export async function unmount() {
if (!app) {
return;
}
app.$destroy();
app.$el.innerHTML = "";
app = null;
}
```
#### 设置路由前缀
```js
const router = new VueRouter({
mode: "history",
base: window.__POWERED_BY_QIANKUN__ ? "/vue2-app/" : process.env.BASE_URL,
routes,
});
```
#### 修改打包配置(`vue.config.js`)
```js
const { defineConfig } = require("@vue/cli-service");
const { name } = require("./package.json");
module.exports = defineConfig({
...,
devServer: {
headers: {
"Access-Control-Allow-Origin": "*",
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: "umd",
jsonpFunction: `webpackJsonp_${name}`,
},
},
});
```
### 1.2、Vue3 + Vite 子应用
`qiankun` 默认不支持 `Vite`,要加载 `Vite` 构建的子应用,需要借助 [vite-plugin-qiankun](https://links.jianshu.com/go?to=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fvite-plugin-qiankun) 插件。
#### 安装 `vite-plugin-qiankun`
```bash
yarn add vite-plugin-qiankun
pnpm add vite-plugin-qiankun
npm i vite-plugin-qiankun
```
#### 公开生命周期函数
```js
import { createApp, type App as VueApp } from "vue";
import {
renderWithQiankun,
qiankunWindow,
type QiankunProps,
} from "vite-plugin-qiankun/dist/helper";
import App from "./App.vue";
import router from "./router";
import "./assets/main.css";
let app: VueApp;
function renderApp(container: any) {
app = createApp(App);
app.use(router);
app.mount(container);
}
if (qiankunWindow.__POWERED_BY_QIANKUN__) {
renderWithQiankun({ bootstrap, mount, unmount, update });
} else {
renderApp("#app");
}
async function bootstrap() {
console.log("vue3 app bootstrap");
}
async function mount(props: QiankunProps) {
renderApp(props.container?.querySelector("#app"));
}
async function unmount() {
app?.unmount();
}
async function update() {
console.log("vue3 app update");
}
```
#### 设置路由前缀,可以不管
```js
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
import { name } from "../../package.json";
const base = qiankunWindow.__POWERED_BY_QIANKUN__ ? name : import.meta.env.BASE_URL;
const router = createRouter({
history: createWebHistory(base),
routes: routes,
});
```
#### 修改打包配置(`vite.config.ts`)
```js
...
import qiankun from "vite-plugin-qiankun";
const port = 8382;
const base = `http://localhost:${port}/`;
export default defineConfig({
base: base,
server: {
port: port,
origin: base,
cors: true,
},
plugins: [
...,
qiankun("vue3-app", {
useDevMode: true,
}),
],
...,
});
```
### 1.3、React 子应用
#### 动态配置资源路径
1. 在 `src` 目录新增 `public-path.js`:
```js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
```
1. 在应用入口 `index.js` 顶部导入 `public-path.js`。
```js
import "./public-path";
```
#### 公开生命周期函数
```js
import "./public-path";
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
function renderRoot(container: HTMLElement) {
ReactDOM.render(
,
container
);
}
if (!window.__POWERED_BY_QIANKUN__) {
renderRoot(document.querySelector("#root")!);
}
export async function bootstrap() {
console.log("react app bootstrap");
}
export async function mount(props: any) {
renderRoot(props.container?.querySelector("#root"));
}
export async function unmount(props: any) {
ReactDOM.unmountComponentAtNode(props.container?.querySelector("#root"));
}
```
#### 设置路由前缀
```js
```
#### 修改 webpack 配置
1. 安装插件 `@rescripts/cli`,当然也可以选择其他的插件,例如 `react-app-rewired`。
```bash
yarn add -D @rescripts/cli
pnpm add -D @rescripts/cli
npm i -D @rescripts/cli
```
1. 应用根目录新增 `.rescriptsrc.js`
```js
const { name } = require("./package");
module.exports = {
webpack: (config) => {
config.output.library = `${name}-[name]`;
config.output.libraryTarget = "umd";
config.output.jsonpFunction = `webpackJsonp_${name}`;
config.output.globalObject = "window";
return config;
},
devServer: (_) => {
const config = _;
config.headers = {
"Access-Control-Allow-Origin": "*",
};
config.historyApiFallback = true;
config.hot = false;
config.watchContentBase = false;
config.liveReload = false;
return config;
},
};
```
1. 修改 `package.json`
```json
- "start": "react-scripts start",
+ "start": "rescripts start",
- "build": "react-scripts build",
+ "build": "rescripts build",
```
# 应用间通信
### 1、props 传参
注册子应用时可以通过 props 传递参数给子应用。
```js
registerMicroApps([
{
name: "Vue2App",
entry: "//localhost:8381/",
container: "#micro-app-container",
activeRule: "/vue2-app",
props: {
nickname: "StoneHui",
age: 30
}
},
]);
```
子应用挂载时可通过 props 获取主应用传递的参数。
```js
export async function mount(props) {
console.log(props.nickname, props.age); // StoneHui 30
}
```
### 2、[initGlobalState(state)](https://links.jianshu.com/go?to=https%3A%2F%2Fqiankun.umijs.org%2Fzh%2Fapi%23initglobalstatestate)
#### 定义全局状态,并返回通信方法,建议在主应用使用,微应用通过 props 获取通信方法。
**相关类型定义:**
```js
/**
* 定义全局状态,并返回通信方法
*
* @param state: 全局状态
* @retuns 通信方法实例
*/
function initGlobalState(state: Record): MicroAppStateActions
/**
* 通信方法
*/
type MicroAppStateActions {
/**
* 在当前应用监听全局状态,有变更触发 callback
* @param callback 状态变更回调函数
* @param fireImmediately,是否立即触发 callback 函数
*/
onGlobalStateChange: (callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void
/**
* 按一级属性设置全局状态,微应用中只能修改已存在的一级属性
* @param callback 变更的全局状态
* @retuns 修改结果
*/
setGlobalState: (state: Record) => boolean
/**
* 移除当前应用的状态监听,微应用 umount 时会默认调用
* @retuns 移除结果
*/
offGlobalStateChange: () => boolean
}
/**
* 全局状态变更的回调函数
* @param state 变更后的全局状态
* @param prevState 变更前的全局状态
*/
type OnGlobalStateChangeCallback = (state: Record, prevState: Record) => void;
```
**使用示例:**
1. 主应用
```js
import { initGlobalState, MicroAppStateActions } from 'qiankun';
const state = {
...
}
// 初始化全局状态并获取操作函数
const actions: MicroAppStateActions = initGlobalState(state);
// 监听全局状态变更
actions.onGlobalStateChange((state, prev) => {
console.log(state, prev);
});
// 更新全局状态
actions.setGlobalState(state);
```
1. 子应用
```js
export function mount(props) {
// 监听全局状态变更
props.onGlobalStateChange((state, prev) => {
console.log(state, prev);
});
// 更新全局状态
props.setGlobalState(state);
}
```
## 3、常见问题
### TypeScript cannot find name `__webpack_public_path__`。
在 `src` 目录新增 `global.d.ts` 文件:
```js
declare let __webpack_public_path__: string;
interface Window {
__POWERED_BY_QIANKUN__: boolean;
__INJECTED_PUBLIC_PATH_BY_QIANKUN__: string;
}
```
### `__webpack_public_path__` 无效,静态资源路径错误。
将 `public-path.js` 的导入语句放在应用入口文件的第一行。
### configuration.output has an unknown property 'jsonpFunction'.
将 `output.jsonpFunction` 更名为 `output.chunkLoadingGlobal`。
```js
// jsonpFunction: `webpackJsonp_${name}`,
chunkLoadingGlobal: `webpackJsonp_${name}`,
```
### 子应用内部路由跳转后无法切换到主应用或其他子应用且路由栈异常。
- `Vue` 通过路由守卫更新 `state`
```js
router.afterEach(() => {
// Vue2
Object.assign(history.state, { current: location.pathname });
// Vue3
Object.assign(history.state, {
current: history.state.current === "/" ? "/" : location.pathname,
});
});
```
- `React` 通过 `popstate` 监听器更新 `state`
```js
window.addEventListener("popstate", () => {
Object.assign(history.state, { current: location.pathname });
});
```