一、创建主应用
创建一个空项目,或者指定一个项目为主应用,下载qiankun
yarn add qiankun # 或者 npm i qiankun -S
二、注册子应用
1.在src/qiankun文件夹中创建index.js文件【以主应用为vite+vue为例】
2.在main.js中引入( import ‘./qiankun’ )
import {registerMicroApps, start} from 'qiankun'
// const getActiveRule = (hash) => (location) => location.hash.startsWith(hash)
registerMicroApps([
{
// 必须与子应用注册名字相同
name: 'micro-vue',
// 入口路径,开发时为子应用所启本地服务,上线时为子应用线上路径
entry: import.meta.env.MODE === 'development' ? '//localhost:7100' : '/micro-vue/',
// 子应用挂载的节点
container: '#subapp-viewport',
// 当访问路由为 /micro-vue 时加载子应用
activeRule: '/micro-vue',
// 主应用向子应用传递参数
props: {
mainMsg: '我是来自主应用的值vue'
}
},
{
name: 'micro-react',
// entry: '//localhost:7101',
entry: import.meta.env.MODE === 'development' ? '//localhost:7101' : '/micro-react/',
container: '#subapp-viewport',
activeRule: '/micro-react',
props: {
msg: '我是来自主应用的值-react'
}
}
])
start({
sandbox: {
strictStyleIsolation: true
},
// 预加载配置
prefetch: true
})
三、整理主应用路由出口
1.#subapp-viewport(容器名称)才会加载注册的子应用
2.isMicroApp 是否为真,来判断是否是主应用路由
3.通用匹配子应用路由,例如 /micro-vue/:pathMatch(.)
4.至此主应用工作准备完成
1.以App.vue为例
<template>
<div id="app">
<div class="header">
<router-link to="/">主页</router-link>
<router-link to="/micro-react">React 应用</router-link>
<router-link to="/micro-vue">Vue 应用</router-link>
</div>
<div class="main-content">
<!-- 主应用自身路由出口 -->
<router-view v-if="!$route.meta.isMicroApp" />
<!-- 子应用挂载容器 -->
<div id="subapp-viewport" v-else></div>
</div>
</div>
</template>
2.router/index.js
import {createRouter, createWebHistory} from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Layout from '../views/layout/index.vue'
import MicroAppContainer from '@/components/MicroAppContainer.vue' // 空壳,区分主应用还是微应用
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'layout',
redirect: '/home',
component: Layout,
meta: {
isMicroApp: false
},
children: [
{
path: 'home',
name: 'home',
component: HomeView,
meta: {
isMicroApp: false
}
},
{
path: 'about',
name: 'about',
component: () => import('../views/AboutView.vue'),
meta: {
isMicroApp: false
}
}
]
},
{
path: '/micro-vue/:pathMatch(.*)*',
name: 'micro-vue',
component: MicroAppContainer,
meta: {
isMicroApp: true
},
},
{
path: '/micro-react/:pathMatch(.*)*',
name: 'micro-react',
component: MicroAppContainer,
meta: {
isMicroApp: true
}
}
]
})
export default router
四.子应用1 micro-vue (以vite+vue项目为例)
1.检查package.json文件中的name,是不是micro-vue
2.npm i vite-plugin-qiankun 插件
3.更改main.js,区分是作为qiankun子应用还是自主启动访问
4.更改vite.config.js中配置,针对 origin、port、base、plugins的更改
5.子应用1的配置完成,主应用通过/micro-vue访问子应用
1.main.js中代码样例参考
import {createApp} from 'vue'
import {createPinia} from 'pinia'
import App from './App.vue'
import router from './router'
import {qiankunWindow, renderWithQiankun} from 'vite-plugin-qiankun/dist/helper'
import {useUserStore} from '@/stores/user'
let app: any
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
createApp(App).use(createPinia()).use(router).mount('#app')
} else {
renderWithQiankun({
mount(props) {
app = createApp(App)
app.use(createPinia())
app.use(router)
//在挂载前调用pinia 中的函数,进行zhu应用跟子应用的传值
const {changeMsg} = useUserStore()
changeMsg(props.mainMsg)
app.mount(props.container.querySelector('#app'))
console.log('子应用挂在了', qiankunWindow.__POWERED_BY_QIANKUN__)
},
bootstrap() {
console.log('[子应用] vue app bootstraped')
},
unmount(props: any) {
if (app) {
app.unmount()
}
}
})
}
2.vite.config.js中的配置参考
import {fileURLToPath, URL} from 'node:url'
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun'
export default defineConfig(({mode}) => ({
base: mode === 'development' ? '/micro-vue' : '/micro-vue',
plugins: [
vue(),
qiankun('/micro-vue', {
// 子应用名字,与主应用注册的子应用名字保持一致
useDevMode: true
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
host: '0.0.0.0', // 主机地址
port: 7100, // 端口
origin: 'http://localhost:7100',
open: false, // 是否自动打开浏览器
hmr: true, // 启用热更新
proxy: {}
},
build: {
outDir: 'micro-vue' //想要把dist修改成什么名字在这边改
}
}))
五.子应用2 micro-react (以webpack+react项目为例)
1.检查package.json文件中的name,是不是micro-react
2.参考官网给的webpack5的配置
3.创建public-path.js(内容如下)在index.js中引入
4.更改index.js,区分是作为qiankun子应用还是自主启动访问,这里注意下window的挂载是不是在子应用挂载前就存在了
5.更改craco.config.js中配置
6.路由加了basename
7.子应用2的配置完成,主应用通过/micro-react访问子应用
1.public-path.js参考
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
2.index.js
import React from "react"
import ReactDOM from "react-dom/client"
import "./index.scss"
import reportWebVitals from "./reportWebVitals"
import { BrowserRouter, RouterProvider } from "react-router-dom"
import router from "./router"
import { Provider } from "react-redux"
import { store } from "./store"
import "./public-path.js"
let root
function render(props) {
const { container } = props
const dom = container
? container.querySelector("#root")
: document.getElementById("root")
root = ReactDOM.createRoot(dom)
root.render(
<Provider store={store}>
<RouterProvider router={router}></RouterProvider>
</Provider>
)
}
// 是否是qiankun 环境下
if (!window.__POWERED_BY_QIANKUN__) {
render({})
}
export async function bootstrap() {
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
render(props)
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
root.unmount()
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log("update props", props)
}
window.qiankunLifecycle = {
bootstrap,
mount,
unmount,
update,
}
reportWebVitals()
3.craco.config.js配置参考
const path = require("path")
const packageName = require("./package.json").name
const isDevelopment = process.env.NODE_ENV === "development"
module.exports = {
// webpack配置
webpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: "umd",
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
globalObject: "window",
filename: "[name].bundle.js",
clean: true,
publicPath: isDevelopment ? "//localhost:7101/" : "/micro-react/",
},
// 配置别名
alias: {
// 约定制定
"@": path.resolve(__dirname, "src"),
},
},
devServer: {
host: "0.0.0.0", // 主机地址
port: 7101, // 端口
open: false, // 是否自动打开浏览器
hot: true, // 启用热更新
proxy: {
},
},
}
4.router/index.js
import React, { lazy } from "react"
import { createBrowserRouter } from "react-router-dom"
import Layout from "@/pages/layout"
import Login from "@/pages/login"
import Home from "../pages/home"
// 区分环境
const basename = window.__POWERED_BY_QIANKUN__ ? "/micro-react" : "/"
const router = createBrowserRouter(
[
{
path: "/",
element:<Layout /> ,
children: [
{
path: "home",
element: <Home />,
},
],
},
{
path: "/login",
element: <Login />,
},
],
{
basename: basename,
}
)
export default router
六、路由跳转并传参
1.router 提供的跳转方法,只支持在本应用中跳转,但不支持跨应用
2.以react跳转vue应用并传参为例
3.在react中添加按钮并添加点击事件
4.在vue项目中接收
1.react中传递
const goVue = () => {
window.history.pushState({ reactMsg: "你好vue" }, "", "/micro-vue")
}
2.vue项目中接收
onMounted(() => {
console.log('vue页面挂载了', window.history)
msg.value = window.history.state.reactMsg
})
七、通过stores管理多个应用的状态
1.通过主应用的store,给子应用传递一个函数,子应用在mount接收并保存,在合适的页面调用,触发,通过主应用分发到其他子应用(场景不是很多见)
八、使用Ant Design、Element组件库出现弹框样式问题配置
1.在主应用中这样设置规则
sandbox: {
strictStyleIsolation: false, // 基于浏览器原生功能 Shadow DOM[它允许将一个隐藏的、独立的 DOM 树附加到常规的 DOM 节点中]
experimentalStyleIsolation: true // 利用 PostCSS 插件,在子应用的样式规则上添加一个 限定选择器
}
2.ant-design中提供了ConfigProvider
<ConfigProvider
getPopupContainer={getPopupContainer}
calendar={() => getPopupContainer}
>
<RouterProvider router={router}></RouterProvider>
</ConfigProvider>
3.针对element plus 我没有做什么更改
4.后续在补充下element的
qiankun搭建遇到的问题总结
1.组件库中弹框样式问题
Error with push/replace State SecurityError: Failed to execute ‘replaceState’ on ‘History’: A history state object with URL ‘http://localhost:5173undefined/’ cannot be created in a document with origin

我在主应用中路由跳转的时候使用了push,保留浏览历史,但是我上个容器已经消失了,没有重新创建,改用replace跳转解决这个问题
2.路由版本问题
1.提供base或者basename的时候,注意路由版本,以及提供的位置
3.挂载容器不存在排查问题
1.路由是不是匹配
2.是不是处在qiankun环境下等
3133

被折叠的 条评论
为什么被折叠?



