主应用vite+react
我们先使用 registerMicroApps
:
- 用途: 用于预先声明一组微应用。
- 激活: 依赖
activeRule
(路由匹配)在 URL 变化时自动加载和挂载/卸载应用。 - 生命周期: 根据
activeRule
和start()
调用,自动处理加载、挂载和卸载阶段。 - 最适合: 大多数常见场景,即通过 URL 路由应用并以声明方式管理其生命周期。
配置方式:
js
//main.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { registerMicroApps, start } from "qiankun";
//注册子应用 自动加载
registerMicroApps([
{
name: "reactApp", // 子应用名称,必须与子应用导出的生命周期函数中的 name 一致
// 修改 entry 为 Webpack 子应用的入口
entry: "//localhost:7100", // Webpack 子应用的开发服务器地址
container: "#sub-app-container", // 子应用挂载的容器
activeRule: "/react", // 激活规则,当路径匹配到 /react 时加载 reactApp
props: {
// 可以传递给子应用的props
msg: "Hello from main app (React Webpack)",
},
},
{
name: "vueApp", // 子应用名称
entry: "//localhost:7200",
container: "#sub-app-container",
activeRule: "/vue",
props: {
msg: "Hello from main app (Vue)",
},
},
]);
// 启动 qiankun
start({
sandbox: {
// 开启沙箱,默认开启
// strictStyleIsolation: true, // 开启严格的样式隔离
experimentalStyleIsolation: true, // 开启实验性的样式隔离 (推荐)
},
});
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
js
//app.js
import "./App.css";
function App() {
const goToReact = () => {
window.history.pushState(null, "", "/react");
};
const goToVue = () => {
window.history.pushState(null, "", "/vue");
};
return (
<>
<h1>Qiankun Main App (React Vite)</h1>
<nav>
<button onClick={goToReact}>Go to React App</button>
<button onClick={goToVue}>Go to Vue App</button>
</nav>
<div id="sub-app-container"></div> {/* 子应用挂载容器 */}
</>
);
}
export default App;
最后效果
- 子应用react

*子应用vue

loadMicroApp
:
-
用途: 用于命令式地加载和渲染单个微应用。
-
激活: 当你需要加载应用时,手动调用该函数,没有自动的
activeRule
匹配。 -
控制: 返回一个
App
实例,允许你手动调用mount()
、unmount()
、update()
等方法。 -
最适合:
- 由 URL 变化以外的事件(例如按钮点击、状态变化)触发的微应用。
- 需要动态加载或卸载而无需完整页面刷新的微应用。
- 需要直接控制应用生命周期的更复杂场景。
下面进入改造 mian.js主文件就不去加载注册了将注册等放到对应的组件去精细化管理
js
//main.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import { registerMicroApps, start } from "qiankun";
import "./index.css";
// 启动 qiankun
start({
sandbox: {
// 开启沙箱,默认开启
// strictStyleIsolation: true, // 开启严格的样式隔离
experimentalStyleIsolation: true, // 开启实验性的样式隔离 (推荐)
},
});
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
还是以app.js为例
js
// my-qiankun-main/src/App.tsx
import React, { useEffect, useState, useRef } from "react";
import { loadMicroApp } from "qiankun"; // 导入 loadMicroApp 和 MicroApp 类型
import "./App.css";
function App() {
const [activeApp, setActiveApp] = useState(null);
// 使用 ref 来存储当前激活的微应用实例
const currentMicroAppRef = useRef(null);
// 用于加载和挂载特定微应用的函数
const loadAndMountApp = (appName, entry) => {
// 1. 卸载任何当前激活的微应用
if (currentMicroAppRef.current) {
currentMicroAppRef.current.unmount();
currentMicroAppRef.current = null; // 卸载后清空 ref
}
const actualEntry = entry;
const appNameForQiankun =
appName === "react"
? "my-qiankun-react-sub-webpack"
: "my-qiankun-vue-sub-webpack-my-qiankun-vue-sub-webpack"; // 关键:这里要匹配 Vue CLI `vue.config.js` 中 `output.library` 的值
// 2. 加载并挂载新的微应用
// 'name' 属性至关重要,必须与子应用构建配置中(例如 webpack 的 output.library name)的 'name' 匹配
const app = loadMicroApp({
name: appNameForQiankun,
entry: actualEntry,
container: "#sub-app-container",
props: {
msg: `Hello from main app (${appName})`,
// 你可以在这里传递更多 props
},
});
// 将 app 实例存储在 ref 中
currentMicroAppRef.current = app;
// 可选:如果你需要更精细的控制,可以在这里显式调用 mount(),
// 但 loadMicroApp 通常会在加载后自动进行初始挂载。
// app.mount(); // 通常由 loadMicroApp 隐式完成初始挂载
// 更新激活应用的状态
setActiveApp(appName);
};
const goToReact = () => {
loadAndMountApp("react", "//localhost:7100"); // 使用 Webpack React 子应用的入口 URL
};
const goToVue = () => {
loadAndMountApp("vue", "//localhost:7200/"); // 确保是 / 结尾,指向 index.html
};
// 可选:当主应用卸载时(例如页面刷新/关闭),卸载当前应用
useEffect(() => {
return () => {
if (currentMicroAppRef.current) {
currentMicroAppRef.current.unmount();
currentMicroAppRef.current = null;
}
};
}, []);
return (
<>
<h1>Qiankun 主应用 (React Vite) - 手动加载</h1>
<nav>
<button onClick={goToReact}>加载 React (Webpack) 应用</button>
<button onClick={goToVue}>加载 Vue (Vite) 应用</button>
</nav>
{activeApp && <p>当前已加载:{activeApp} 应用</p>}
<div id="sub-app-container"></div> {/* 子应用挂载容器 */}
</>
);
}
export default App;
这样就做到插拔式的,随用随加载,可以放到某个组件中自己控制子应用的加载。
然后是react子应用的配置
js
//webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { name } = require("./package.json"); // 获取子应用名称
module.exports = {
mode: "development", // 或 'production'
entry: "./src/main.tsx", // 子应用入口文件
output: {
filename: "js/[name].js", // 打包后的文件名
path: path.resolve(__dirname, "dist"), // 打包目录
library: `${name}-[name]`, // 必填,子应用名称,用于 Qiankun 查找
libraryTarget: "umd", // 打包为 umd 格式,Qiankun 推荐
chunkLoadingGlobal: `webpackJsonp_${name}`, // Webpack 5 唯一标识,防止冲突
globalObject: "window", // 定义全局变量,兼容 node 和 browser 环境
publicPath: "//localhost:7100/", // 重点:子应用的运行地址
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"], // 支持的文件扩展名
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
cacheDirectory: true, // 开启缓存
},
},
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html", // HTML 模板文件
filename: "index.html", // 打包后输出的文件名
chunks: ["main"], // 引入 main chunk
}),
],
devServer: {
port: 7100, // 子应用开发服务器端口
historyApiFallback: true, // 解决单页应用路由刷新问题
headers: {
"Access-Control-Allow-Origin": "*", // 允许所有来源访问,解决跨域问题
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"X-Requested-With, content-type, Authorization",
},
client: {
overlay: false, // 不在浏览器显示全屏错误覆盖
},
},
};
js
//main.js
import React from "react";
import ReactDOM from "react-dom/client"; // 使用 createRoot for React 18+
import App from "./App";
import "./index.css";
let root: ReactDOM.Root | null = null; // 用于存储 React 18 的 root 实例
function render(props: any) {
const { container } = props;
const mountNode = container
? container.querySelector("#root")
: document.getElementById("root");
if (mountNode) {
if (!root) {
// 首次挂载创建 root
root = ReactDOM.createRoot(mountNode);
}
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
}
}
// 在非微前端环境下独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
// Qiankun 生命周期
export async function bootstrap() {
console.log("[Webpack-React] child app bootstraped");
}
export async function mount(props: any) {
console.log("[Webpack-React] child app mounted", props);
render(props);
}
export async function unmount(props: any) {
console.log("[Webpack-React] child app unmount", props);
if (root) {
root.unmount(); // 卸载 React 应用
root = null; // 清空 root 实例
}
}
然后是vue子应用的配置
js
// vue.config.js
const { name } = require("./package.json"); // 自动获取项目名称
module.exports = {
// 重要的配置:子应用运行的公共路径,与主应用中注册的 entry 对应
publicPath: "//localhost:7200/",
outputDir: "dist", // 打包输出目录
assetsDir: "static", // 静态资源目录
filenameHashing: true, // 生产环境打包文件使用哈希值
devServer: {
port: 7200,
headers: {
"Access-Control-Allow-Origin": "*", // 允许跨域访问,解决主应用加载子应用时的跨域问题
},
// historyApiFallback: true, // 如果子应用使用了 Vue Router 的 history 模式,可以启用
},
// Webpack 链式配置,用于 Qiankun
configureWebpack: {
output: {
// 必填项:一个全局唯一的值,用于 Qiankun 查找子应用导出的生命周期函数
// 通常使用 package.json 中的 name 加上一个后缀
library: `${name}-[name]`,
libraryTarget: "umd", // **关键:打包为 UMD 格式**
// Webpack 5 推荐使用 chunkLoadingGlobal 替换旧的 jsonpFunction,避免多个微前端之间的冲突
chunkLoadingGlobal: `webpackJsonp_${name}`,
globalObject: "window", // 定义全局变量,兼容 node 和 browser 环境
},
},
};
js
//main.js
// my-qiankun-vue-sub-webpack/src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
// import "./assets/main.css"; // 导入样式
let instance = null; // 用于存储 Vue 应用实例
function render(props = {}) {
const { container } = props;
// 将应用挂载到主应用提供的容器或默认的 #app 元素
instance = createApp(App);
instance.mount(container ? container.querySelector("#app") : "#app");
}
// 在非微前端环境下独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
// Qiankun 生命周期钩子
export async function bootstrap() {
console.log("[Vue-Webpack] child app bootstraped");
}
export async function mount(props) {
console.log("[Vue-Webpack] child app mounted", props);
render(props); // 挂载应用
}
export async function unmount(props) {
console.log("[Vue-Webpack] child app unmount", props);
if (instance) {
instance.unmount(); // 卸载 Vue 应用实例
instance = null;
}
}
// 可选:如果子应用内部有路由,并且希望通知主应用
// export async function update(props: any) {
// console.log('[Vue-Webpack] child app updated', props);
// }
这样就做到了,将旧项目集成到现有的项目中了,最后说下,部署到生产上需要注意的地方
- URL 一致性: 确保基座中配置的子应用
entry
URL 与子应用实际部署的 URL 完全一致,并且子应用内部的publicPath
也与其部署 URL 一致。协议 (http/https
)、域名、端口和路径都必须匹配。
js
// my-qiankun-react-sub-webpack/webpack.config.js
module.exports = {
mode: 'production', // 确保是生产模式
// ...
output: {
// ... 其他配置
publicPath: '//yourcdn.com/react-sub-app/', // 生产环境的部署路径
// 或者如果你只知道相对路径,例如基座部署在 /portal,子应用在 /portal/react-sub-app
// 那么 publicPath 可以是 'auto' (Webpack 5 自动推断) 或者 './' (如果都是相对路径)
// 但最安全的是使用绝对 URL
},
// ...
};
// my-qiankun-vue-sub-webpack/vue.config.js
module.exports = {
publicPath: '//yourcdn.com/vue-sub-app/', // 生产环境的部署路径
outputDir: 'dist',
// ...
};
//主应用main.js
// 示例:使用环境变量来设置 entry
const isProd = process.env.NODE_ENV === 'production';
const reactEntry = isProd ? 'https://yourcdn.com/react-sub-app/' : '//localhost:7100/';
const vueEntry = isProd ? 'https://yourcdn.com/vue-sub-app/' : '//localhost:7200/';
// ...然后在 loadMicroApp 或 registerMicroApps 中使用 reactEntry 和 vueEntry
- CORS: 生产环境中也需要处理 CORS。如果基座和子应用部署在不同的域名或子域名下(例如基座在
yourdomain.com
,子应用在cdn.yourdomain.com
),子应用服务器(CDN 或你的 Web 服务器)需要配置正确的Access-Control-Allow-Origin
响应头,以允许基座加载子应用的资源。 - 历史模式路由: 如果你的基座或子应用使用了前端路由的
history
模式,那么你的 Web 服务器(如 Nginx、Apache)必须配置路由回退,将所有不匹配静态文件的请求都指向index.html
,否则页面刷新会导致 404 错误。
js
**Nginx 配置:** 如果你使用的是 Nginx 等 Web 服务器,
你需要配置正确的路由规则,
确保当请求到达 `https://yourdomain.com/react-sub-app/` 时,
Nginx 能够正确地指向你的 React 子应用的 `dist` 目录。
例如一个简单的 Nginx 配置片段:
server {
listen 80;
server_name yourdomain.com; # 替换为你的域名
# 基座应用
location / {
root /path/to/my-qiankun-main/dist; # 基座应用的 dist 目录
index index.html;
try_files $uri $uri/ /index.html; # 单页应用路由回退
}
# React 子应用
location /react-sub-app/ {
alias /path/to/my-qiankun-react-sub-webpack/dist/; # React 子应用的 dist 目录
index index.html; # 如果子应用有自己的 index.html
try_files $uri $uri/ /react-sub-app/index.html; # 如果子应用是单页应用
}
# Vue 子应用
location /vue-sub-app/ {
alias /path/to/my-qiankun-vue-sub-webpack/dist/; # Vue 子应用的 dist 目录
index index.html;
try_files $uri $uri/ /vue-sub-app/index.html;
}
# 如果你的子应用是通过 CDN 访问,这里就不需要配置子应用的 location 块,
# 而是直接将子应用的 dist 目录上传到 CDN 服务商。
}
- 样式隔离: 再次确认主应用中
experimentalStyleIsolation: true
已经启用,确保样式不会互相污染。 - JS 沙箱: 确认 JS 沙箱工作正常,避免全局变量污染。
- 错误日志: 在生产环境部署后,务必检查服务器日志和浏览器控制台,确保没有隐藏的错误。
生产环境的部署是一个细致的过程,每一步都必须精确无误。建议在上线前,先在预生产环境或测试环境进行充分的测试。