项目概述
本教程基于实际项目演示如何使用 qiankun 构建微前端架构。项目包含:
- 主应用 (vue-project): Vue 3 + TypeScript + Element Plus
- 子应用: React、Vue、Umi 三个不同技术栈的应用
目录结构
qiankun/
├── vue-project/ # 主应用 (Vue 3)
├── react-app/ # React 子应用
├── vue-app/ # Vue 子应用
├── umi-app/ # Umi 子应用
└── micro-base/ # 基础配置
一、主应用配置
1.1 安装依赖
npm install qiankun
1.2 主应用入口文件 (main.ts)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './qiankun'
import 'virtual:svg-icons-register'
import components from '@/components/index'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import '@/styles/index.scss'
const app = createApp(App)
app.use(ElementPlus)
app.use(components)
app.use(router)
app.mount('#app')
1.3 Qiankun 配置文件 (qiankun.ts)
import { start, registerMicroApps, initGlobalState } from 'qiankun'
const apps = [
{
name: 'sub-react',
entry: '//localhost:8080',
activeRule: '/sub-react',
container: '#sub-app'
},
{
name: 'sub-vue',
entry: '//localhost:3002',
activeRule: '/sub-vue',
container: '#sub-app'
},
{
name: 'sub-umi',
entry: '//localhost:3003',
activeRule: '/sub-umi',
container: '#sub-app'
}
]
registerMicroApps(apps, {
beforeLoad: [async app => console.log('before load', app.name)],
beforeMount: [async app => console.log('before mount', app.name)],
afterMount: [async app => console.log('after mount', app.name)]
})
const state = { count: 1 }
const actions = initGlobalState(state)
actions.onGlobalStateChange((state, prev) => {
console.log('全局状态变化:', state, prev)
})
actions.setGlobalState(state)
start()
1.4 主应用路由配置
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
},
{
path: '/sub-react/:pathMatch(.*)*',
name: 'SubReact',
component: () => import('@/views/SubApp.vue')
},
{
path: '/sub-vue/:pathMatch(.*)*',
name: 'SubVue',
component: () => import('@/views/SubApp.vue')
},
{
path: '/sub-umi/:pathMatch(.*)*',
name: 'SubUmi',
component: () => import('@/views/SubApp.vue')
}
]
export default createRouter({
history: createWebHistory(),
routes
})
1.5 子应用容器组件
<template>
<div id="sub-app"></div>
</template>
<script setup lang="ts">
</script>
二、子应用配置
2.1 React 子应用配置
安装依赖
npm install @rescripts/cli --save-dev
修改 package.json
{
"scripts": {
"start": "rescripts start",
"build": "rescripts build"
}
}
创建 .rescriptsrc.js
const { name } = require('./package')
module.exports = {
webpack: (config) => {
config.output.library = `${name}-[name]`
config.output.libraryTarget = 'umd'
config.output.globalObject = 'window'
config.output.chunkLoadingGlobal = `webpackJsonp_${name}`
return config
},
devServer: (_) => {
const config = _
config.headers = {
'Access-Control-Allow-Origin': '*'
}
config.historyApiFallback = true
config.hot = false
config.liveReload = false
return config
}
}
修改入口文件 src/index.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
let root
function render(props) {
const { container } = props
const dom = container ? container.querySelector('#root') : document.querySelector('#root')
root = ReactDOM.createRoot(dom)
root.render(<App />)
}
if (!window.__POWERED_BY_QIANKUN__) {
render({})
}
export async function bootstrap() {
console.log('[react16] react app bootstraped')
}
export async function mount(props) {
console.log('[react16] props from main framework', props)
render(props)
}
export async function unmount(props) {
const { container } = props
const dom = container ? container.querySelector('#root') : document.querySelector('#root')
root && root.unmount()
}
2.2 Vue 子应用配置
修改 vue.config.js
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
globalObject: 'window',
chunkLoadingGlobal: `webpackJsonp_${name}`
}
},
devServer: {
port: 3002,
headers: {
'Access-Control-Allow-Origin': '*'
}
}
})
修改入口文件 src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
let instance = null
function render(props = {}) {
const { container } = props
instance = createApp(App)
instance.use(router)
instance.mount(container ? container.querySelector('#app') : '#app')
}
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap() {
console.log('[vue] vue app bootstraped')
}
export async function mount(props) {
console.log('[vue] props from main framework', props)
render(props)
}
export async function unmount() {
instance.unmount()
instance._container.innerHTML = ''
instance = null
}
2.3 Umi 子应用配置
修改 .umirc.ts
import { defineConfig } from 'umi'
export default defineConfig({
nodeModulesTransform: {
type: 'none'
},
routes: [
{ path: '/', component: '@/pages/index' }
],
fastRefresh: {},
qiankun: {
slave: {}
},
base: '/sub-umi/',
publicPath: '/sub-umi/'
})
三、全局状态管理
3.1 主应用中使用全局状态
import { initGlobalState } from 'qiankun'
const initialState = {
user: { name: 'admin', token: 'xxx' },
theme: 'light'
}
const actions = initGlobalState(initialState)
actions.onGlobalStateChange((state, prev) => {
console.log('主应用监听到状态变化:', state, prev)
})
actions.setGlobalState({
user: { name: 'user1', token: 'yyy' }
})
3.2 子应用中使用全局状态
export async function mount(props) {
const { onGlobalStateChange, setGlobalState } = props
onGlobalStateChange((state, prev) => {
console.log('子应用监听到状态变化:', state, prev)
})
setGlobalState({
theme: 'dark'
})
render(props)
}
四、应用间通信
4.1 主应用向子应用传递数据
registerMicroApps([
{
name: 'sub-react',
entry: '//localhost:8080',
activeRule: '/sub-react',
container: '#sub-app',
props: {
data: { message: 'Hello from main app' },
methods: {
onRouteChange: (path) => {
console.log('子应用路由变化:', path)
}
}
}
}
])
4.2 子应用接收数据
export async function mount(props) {
const { data, methods } = props
console.log('接收到主应用数据:', data)
methods.onRouteChange('/sub-react/page1')
render(props)
}
五、样式隔离
start({
sandbox: {
strictStyleIsolation: true
}
})
六、部署配置
6.1 开发环境启动
cd vue-project && npm run dev
cd react-app && npm start
cd vue-app && npm run serve
cd umi-app && npm run dev
6.2 生产环境部署
server {
listen 80;
server_name example.com;
location / {
root /var/www/main-app;
try_files $uri $uri/ /index.html;
}
location /sub-react {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
}
location /sub-vue {
proxy_pass http://localhost:3002;
proxy_set_header Host $host;
}
location /sub-umi {
proxy_pass http://localhost:3003;
proxy_set_header Host $host;
}
}
七、常见问题与解决方案
7.1 跨域问题
devServer: {
headers: {
'Access-Control-Allow-Origin': '*'
}
}
7.2 路由问题
const router = new VueRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? '/sub-vue' : '/',
routes
})
7.3 静态资源加载问题
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
八、最佳实践
- 应用拆分原则:按业务域拆分、技术栈独立、团队独立开发
- 性能优化:预加载子应用、资源共享、缓存策略
- 错误处理:全局错误捕获、降级处理
总结
通过本教程,你已经学会了:
- 如何配置 qiankun 主应用
- 如何改造现有应用为 qiankun 子应用
- 应用间通信和状态管理
- 样式隔离和部署配置
- 常见问题的解决方案
qiankun 为微前端架构提供了完整的解决方案,让不同技术栈的应用能够无缝集成。