qiankun使用指南
主要记录qiankun如何配置主应用与vite构建的微应用。
前提背景
qiankun是个什么东西
简单来说就是一个把不同应用放到一个容器里面的技术,可以简单的理解为iframe的升级版(当然不可能这么简单)。
qiankun默认支持的是什么
qiankun主应用随便什么都可以,只要能添加qiankun能支持。但微应用默认只能支持webpack打包的,vite是不支持的,想要支持则需要添加插件(至于为什么不支持,这里不多赘述,有很多文档)。
不支持vite的解决办法
vite-plugin-qiankun解决不支持 + vite-plugin-css-injected-by-js解决样式冲突
具体使用
实现目标主应用和微应用打包后,给予同域名进行显示
主应用
安装依赖
yarn add qiankun
添加qiankun主应用的配置文件
import { registerMicroApps } from 'qiankun'
import { IResponseType } from '@/types/api.ts'
interface StartQiankunProps {
menus: IResponseType['data']
}
const startQiankun = (props: StartQiankunProps) => {
const defaultProps = {
token: localStorage.getItem('auth_token'),
// 这里面就放要传到子组件的参数
}
// 1. 要加载的子应用列表
const micors = [
{
name: 'sub', // 子应用的名称
entry: '//localhost/sub/index.html', // 默认会加载这个路径下的html 这是nginx的路径
activeRule: '/qiankunSub', // 匹配的路由
container: '#qiankunSub', // 加载的容器
props: {
...defaultProps
}
}
// 这里可以参照着继续加其他的子应用
]
registerMicroApps(micors, {
beforeLoad: [
(app) => {
console.log('[LifeCycle] before load %c%s', 'color: green;', app.name)
return Promise.resolve()
}
],
beforeMount: [
(app) => {
console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)
return Promise.resolve()
}
],
afterUnmount: [
(app) => {
console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
return Promise.resolve()
}
]
})
}
export default startQiankun
接着要添加给微应用的存放的容器,建议这个容器保持存在,不然删除添加会导致微应用不断卸载加载,极其浪费,甚至产生各种bug
<div id='sub'></div>
在确保容器存在后,启动qiankun
import startQiankun from '@/utils/qiankunConfig.ts'
// 确保容器渲染完成后启动 qiankun
startQiankun({
//这里可放你要传的数据
}) // startQiankun可以提前执行,放哪随意,看法业务逻辑
if (document.querySelector('#sub')) {
start();
}
微应用(特指vite构建的)
添加依赖
yarn add vite-plugin-qiankun vite-plugin-css-injected-by-js
在vite.config.ts上配置
import qiankun from 'vite-plugin-qiankun'
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
export default defineConfig(({ mode }) => {
return {
// 当然你可以这样配置 base: VITE_APP_ENV === 'production' ? '/' : '/',
base: '/sub', // 指向的是nginx要存放的路径地址 -> 主应用下的/sub
plugins: [
react(),
qiankun('qiankunSub', { // 这是主应用要指向的子应用名字
useDevMode: true
}),
cssInjectedByJsPlugin(),
],
}
})
改造main.tsx
import { createRoot, Root } from 'react-dom/client'
import App from './App'
import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper'
import QiankunProvider from '@/components/QiankunProvider'
declare global {
interface Window {
QIANKUN__MANAGER?: boolean
}
}
// 用于保存 React 根实例
let app: Root | null = null
// 初始化 Qiankun 子应用
const initQianKun = () => {
renderWithQiankun({
// 子应用初始化钩子
bootstrap() {
console.log('微应用:bootstrap')
},
// 子应用挂载钩子
mount(props) {
console.log('微应用:mount', props)
render(props) // 调用渲染函数
},
// 子应用卸载钩子
unmount(props) {
console.log('微应用:unmount', props)
if (app) {
app.unmount() // 卸载 React 应用
app = null
}
},
// 子应用更新钩子
update(props) {
console.log('微应用:update', props)
}
})
}
// 渲染函数,根据是否传入容器决定渲染位置
function render(props: QiankunProps) {
const { container } = props
const rootContainer = container
? container.querySelector('#root') // 使用主应用传入的容器
: document.getElementById('root') // 默认容器
if (!rootContainer) {
console.error('无法找到 #root 容器')
return
}
// 如果未创建过 app 实例,则创建
if (!app) {
app = createRoot(rootContainer)
}
// 渲染主应用
app.render(
<QiankunProvider {...props}>
<App />
</QiankunProvider>
)
}
// 如果不是通过 Qiankun 加载的独立运行模式
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
// window.QIANKUN__MANAGER = true
render({}) // 直接渲染主应用
} else {
window.QIANKUN__MANAGER = qiankunWindow.__POWERED_BY_QIANKUN__
initQianKun() // 启动 Qiankun 子应用模式
}
套一个 QiankunProvider组件用来处理主应用传过来的props,并传递下去
import { createContext, useEffect, useState } from 'react'
import { QiankunProps } from 'vite-plugin-qiankun/dist/helper'
import { cloneDeep } from 'lodash'
export const QiankunContext = createContext<QiankunProps>({
token: '',
})
const QiankunProvider = (props: QiankunProps) => {
const [data, setData] = useState(props)
const handleInit = () => {
const newData = cloneDeep(props)
// 这里就可以处理主应用传入的props了
setPostData(newData)
}
useEffect(() => {
if (window.QIANKUN__MANAGER) {
handleInit()
}
}, [])
return <QiankunContext.Provider value={{ ...data}}>{props.children}</QiankunContext.Provider>
}
export default QiankunProvider
如此,一切就搞定了,之后就根据业务需求添加相关东西
nginx
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
# 主应用
location / {
root html\dist; # 主应用路径
index index.html;
try_files $uri /index.html;
}
# 子应用1
location /qiankunSub {
root html\dist\sub; # 子应用路径
index index.html;
try_files $uri /index.html;
}
}
}
就此搞定