微前端qiankun+vite+Vue3+React 实践指南
前言
因为之前刚好有好几个项目因为项目比较大,子模块应用也很复杂,部分子模块要求能够独立运行独立部署,而且后期可能还会有其他子模块持续接入,所以经过技术分析选型,最后用到了 微前端
框架qiankun
,在实际过程中体验还可以,所以准备写下这篇文章进行一些总结分享。
- 什么是微前端
1.每个子应用可以独立开发、独立部署,不影响其他子应用。
2.各子应用可以使用不同的前端技术栈(如 React、Vue 等)。
3.通过拆分大型项目,降低单个应用的复杂度,提升代码的可维护性。
4.每个子应用之间状态隔离,运行时状态不共享。
- 为什么选择 qiankun
1.qiankun
是基于 single-spa 的微前端解决方案。
2.由于主应用微应用都能做到技术栈无关,qiankun
对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun
的 API 即可完成应用的微前端改造。
3.qiankun
支持 样式隔离,确保微应用之间样式互相不干扰。
4.qiankun
支持资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
5.qiankun
已经发布多年,技术比较成熟稳定,文档齐全。
准备工作
- 因为最近一直用Vue3,所以创建一个
vue3
+vite
+router
+element-plus
项目app-main
作为主应用
主要作用是:用来做统一的登录、子应用菜单切换、个人信息提示、消息通知提示等。
- 创建一个
vue3
+vite
+router
+element-plus
项目app-first
作为子应用1
,运行起来效果如下。
- 创建一个
react
+vite
+router
+antdv
项目app-second
作为子应用2
,运行起来效果如下。
- 项目目录结构大致如下,我把代码上传到了 gitee 上,有需要的掘友可以下载下来,跟着后面思路一起动手试一试。
开始修改配置qiankun
1.修改主项目
- 添加
qiankun
到主项目中。
npm install qiankun
- 在 src 目录下面创建
/common/micro-app.ts
export const microApps = [
{
name: 'first', //
entry:"//localhost:7001", // // import.meta.env.MODE === "dev" ? "//localhost:7002" : "/main/second/",
container: '#page-main-container', // 挂载 子应用的 容器Id
activeRule: '/main/first',
},
{
name: 'second', //
entry:"//localhost:7002", // import.meta.env.MODE === "dev" ? "//localhost:7002" : "/main/second/",
container: '#page-main-container',// 挂载 子应用的 容器Id
activeRule: '/main/second',
},
];
- 修改主应用 router 利用
:pathxx(.*)*
去匹配所有路由
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/',
redirect: '/main/first',
},
{
path: "/login",
name: "Login",
component: () => import("@/views/login.vue"),
},
{
path: "/main/:pathxx(.*)*", //通配所有路由
name: "Main",
meta: {
title: '根路由',
},
component: () => import(/* webpackChunkName: "main" */ '@/views/index.vue'),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
- 修改
src/views/index.vue
注册启动qiankun
微应用 详细参数配置可以参考下qiankun官方文档
<template>
<el-container>
<el-header style="padding: 0;">
<el-menu class="el-menu-demo" mode="horizontal" background-color="#545c64" text-color="#fff"
active-text-color="#ffd04b" @select="handleSelect">
<el-menu-item index="/main/first" @clikc="gotoUrl('/main/first')">子应用1</el-menu-item>
<el-menu-item index="/main/second" @clikc="gotoUrl('/main/second')">子应用2</el-menu-item>
</el-menu>
</el-header>
<div id="page-main-container">
<RouterView></RouterView>
</div>
</el-container>
</template>
<script setup lang="ts">
import { onBeforeMount } from 'vue'
import { microApps } from "@/common/micro-app.ts"
import {
registerMicroApps,
start
} from "qiankun";
const handleSelect=(key:string)=>{
gotoUrl(key)
}
const gotoUrl=(url:string)=>{
console.log('跳转',url)
history.pushState(null, url, url);
}
onBeforeMount(() => {
const windowPbj: any = window
if (!windowPbj.qiankunStarted) {
registerApp();
start();
}
})
// 挂载应用
const registerApp = () => {
registerMicroApps(microApps)
}
</script>
2.修改子应用1 (vue3项目)
- 因为用到了vite框架所以推荐直接用封装好了的插件 vite-plugin-qiankun
npm install vite-plugin-qiankun -D
- 修改子应用的
vite.config.ts
这里需要注意名称要和主应用里面micro-app.ts
注册的name
保持一致
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import qiankun from "vite-plugin-qiankun";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
qiankun('first', {
// 微应用名字,与主应用注册的微应用名字保持一致
useDevMode: true,
}),
],
resolve: {
alias: [
{
find: '@',
replacement: resolve(__dirname, './src'),
},
],
},
server: {
headers: {
"Access-Control-Allow-Origin": "*",
},
host: "0.0.0.0", // ip
port: 7001,
origin: "http://localhost:7001",
},
})
- 因为采用了
history
模式 所以需要修改一下src/routers/index.ts
router 的入口配置,需要注意入口名称和主应用micro-app.ts
配置中activeRule
保持一致。
import { createRouter, createWebHistory } from 'vue-router';
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
const routes = [
{
path: '/',
name: 'Layout',
redirect: '/home',
component: () => import(/* webpackChunkName: "home" */ '@/Layout/index.vue'),
meta: {
title: '根路由',
},
children: [
{
path: '/home',
name: 'Home',
component: () => import(/* webpackChunkName: "home" */ '@/views/index.vue'),
meta: {
title: '首页',
},
},
],
}
];
export const history = createWebHistory(
qiankunWindow.__POWERED_BY_QIANKUN__ ? '/main/first' : "/"
);
const router = createRouter({
history: history,
routes,
});
export default router;
- 修改子应用 main.ts,主要是用
qiankunWindow.__POWERED_BY_QIANKUN__
判断环境,利用renderWithQiankun
注册qiankun, 主要代码如下。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router, { history } from "@/routers/index";
import {
renderWithQiankun,
qiankunWindow
} from 'vite-plugin-qiankun/dist/helper';
let instance: any = null;
async function render(props: any = {}) {
const { container, activeRule } =
props;
instance = createApp(App);
instance.use(router);
instance?.mount(container ? container.querySelector("#app") : "#app");
instance.config.globalProperties.$activeRule = activeRule;
}
const initQianKun = () => {
renderWithQiankun({
mount(props) {
render(props);
},
bootstrap() { },
update() { },
unmount() {
instance.unmount();
instance._container.innerHTML = "";
instance = null;
history.destroy();
},
});
};
if (qiankunWindow.__POWERED_BY_QIANKUN__) {
console.log("qiankun渲染开始");
initQianKun();
} else {
console.log("不是qiankun渲染");
render();
}
运行效果如下
3.修改子应用2 (React项目)
- 因为用到了vite框架所以推荐直接用封装好了的插件 vite-plugin-qiankun,因为它保留了
vite
构建es
模块的优势。
npm install vite-plugin-qiankun -D
- 修改子应用
vite.config.ts
配置,加载vite-plugin-qiankun
插件,因为会和@vitejs/plugin-react
插件冲突 所以要判断环境去掉@vitejs/plugin-react
插件。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'node:path';
import qiankun from "vite-plugin-qiankun";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const isDev = mode === 'development'
return {
plugins: [
// second 要和主应用注册子应用时的名称一样(registerMicroApps的name属性)
qiankun("second", {
useDevMode: true,
}),
!isDev && react(),
],
resolve: {
alias: [
{
find: '@',
replacement: resolve(__dirname, './src'),
},
],
},
server: {
headers: {
"Access-Control-Allow-Origin": "*",
},
host: "0.0.0.0", // ip
port: 7002,
origin: "http://localhost:7002",
},
};
});
- 修改
main.tsx
判断环境利用renderWithQiankun
挂载注册qiankun
。原理和Vue 项目挂载逻辑原理其实差不多,只是具体渲染代码有所不同。
// import './public-path';
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import {
renderWithQiankun,
qiankunWindow,
QiankunProps,
} from 'vite-plugin-qiankun/dist/helper'
let root: ReactDOM.Root | null = null
function render(props: QiankunProps) {
const { container } = props
const root = ReactDOM.createRoot(
(container
? container.querySelector('#root')
: document.getElementById('root')) as HTMLElement
)
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
)
return root
}
const initQianKun = () => {
renderWithQiankun({
mount(props) {
root = render(props)
},
bootstrap() {
console.log('bootstrap')
},
unmount(props) {
root?.unmount()
},
update(props) {
},
})
}
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
root = render({})
} else {
initQianKun();
}
- 修改
App.tsx
,最主要的内容是修改react-router-dom
路由组件BrowserRouter
属性basename
,原理和修改Vue-router
createRouter 差不多,都是为了修改入口配置用来匹配qiankun。
import { Suspense } from "react"
import { BrowserRouter, Routes, Route,Navigate } from "react-router-dom";
import routes, { RouterType } from "./routers"; // Import your route configuration
import React from "react";
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
function App() {
// 由于路由组件是懒加载的,渲染页面可能会有延迟,使用Suspense 可优化交互
const RouteEleMent = (route: RouterType): React.ReactNode => {
if (!route.component) {
return null;
}
return (
<Suspense fallback={<div> Loading...</div>}>
<route.component />
</Suspense>
);
};
// 通过每个路由对象渲染Route
const RouteItem = (route: RouterType) => {
return (
<Route key={route.name} element={RouteEleMent(route)} path={route.path}>
{RouteList(route.children ?? [])}
</Route>
);
};
// 根据配置的routeconfig 生成Route
const RouteList = (list: RouterType[]) => {
return list.map((item) => RouteItem(item));
};
return (
<div className="App">
<BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? "/main/second" : "/"}>
<Routes>
{/* 重定向 */}
<Route path="/" element={<Navigate to="/home" />} />
{RouteList(routes)}
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
运行效果如下
总结
通过上面文章,我们成功地使用 Vue 3 和 Vite 创建了主框架,并集成了 qiankun 来管理和加载 React 18 和 Vue3 子应用。由于篇幅有限,所以这里重点是使用qiankun的应用部署,为了方便大家实践理解,我把源代码放在了gitee上 有需要的小伙伴可以自己下载下来体验一下,如果有遇到问题欢迎评论区或者私信我。
转载自:https://juejin.cn/post/7390689983790104591