qiankun微前端

主应用vite+react

我们先使用 registerMicroApps:

  • 用途: 用于预先声明一组微应用。
  • 激活: 依赖 activeRule(路由匹配)在 URL 变化时自动加载和挂载/卸载应用。
  • 生命周期: 根据 activeRulestart() 调用,自动处理加载、挂载和卸载阶段。
  • 最适合: 大多数常见场景,即通过 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 沙箱工作正常,避免全局变量污染。
  • 错误日志: 在生产环境部署后,务必检查服务器日志和浏览器控制台,确保没有隐藏的错误。

生产环境的部署是一个细致的过程,每一步都必须精确无误。建议在上线前,先在预生产环境或测试环境进行充分的测试。

相关推荐
Net蚂蚁代码1 小时前
Angular入门的环境准备步骤工作
前端·javascript·angular.js
小着3 小时前
vue项目页面最底部出现乱码
前端·javascript·vue.js·前端框架
lichenyang4536 小时前
React ajax中的跨域以及代理服务器
前端·react.js·ajax
呆呆的小草6 小时前
Cesium距离测量、角度测量、面积测量
开发语言·前端·javascript
一 乐7 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
testleaf7 小时前
前端面经整理【1】
前端·面试
好了来看下一题7 小时前
使用 React+Vite+Electron 搭建桌面应用
前端·react.js·electron
啃火龙果的兔子7 小时前
前端八股文-react篇
前端·react.js·前端框架
小前端大牛马7 小时前
react中hook和高阶组件的选型
前端·javascript·vue.js
刺客-Andy8 小时前
React第六十二节 Router中 createStaticRouter 的使用详解
前端·javascript·react.js