1 前言
前言摘抄自 qiankun 官网微前端介绍
1.1 微前端定义
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
1.2 微前端架构核心价值
- 技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 - 独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 - 增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略 - 独立运行时
每个微应用之间状态隔离,运行时状态不共享
2 主应用接入 qiankun
2.1 安装 qiankun
npm i qiankun
2.2 基于路由配置方式接入
- main.ts
import { registerMicroApps, start } from 'qiankun'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
// 注册微应用
registerMicroApps(
[
{
name: 'subApp', // 必须与微应用注册名字相同
entry: 'http://localhost:8000', // 入口路径,开发时为微应用所启本地服务,上线时为微应用线上路径
container: '#sub-app-container', // app.vue 配置的挂载容器 id
activeRule: '/home', // 当访问路由为 home 时加载微应用
props: {
// 主应用向微应用传递参数
}
}
],
{
// 生命周期钩子函数
// beforeLoad: (app) => {
// console.log('beforeLoad', app)
// },
// beforeMount: (app) => {
// console.log('beforeMount ', app)
// },
// afterMount: (app) => {
// console.log('afterMount', app)
// }
// beforeUnmount: (app) => {
// console.log('beforeUnmount ', app)
// },
// afterUnmount: (app) => {
// console.log('afterUnmount', app)
// }
}
)
// 启动 qiankun
start()
- app.vue
<template>
<!-- 提供挂载容器 -->
<div id="sub-app-container"></div>
</template>
2.3 手动加载微应用方式接入
- subApp.vue
<template>
<button @click="loadApp">挂载微应用</button>
<button @click="unloadApp">卸载微应用</button>
<!-- 提供挂载容器 -->
<div id="sub-app-container"></div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { loadMicroApp } from 'qiankun'
let microApp: any = null // 微应用实例
const loadApp = () => {
if (microApp) return
microApp = loadMicroApp({
name: 'subApp', // 必须与微应用注册名字相同
entry: 'http://localhost:8000', // 入口路径,开发时为微应用所启本地服务,上线时为微应用线上路径
container: '#sub-app-container',
props: {
// 主应用向微应用传递参数
}
})
microApp.mountPromise.then(() => {
// 微应用加载完成后回调
})
}
const unloadApp = () => {
if (!microApp) return
microApp.unmount() // 卸载微应用
}
</script>
3 微应用接入 qiankun
3.1 安装 vite-plugin-qiankun
qiankun 暂不支持 Vite 方式接入,需安装 vite-plugin-qiankun
npm i vite-plugin-qiankun
3.2 接入 qiankun
- vite.config.ts
import { defineConfig } from 'vite'
import qiankun from 'vite-plugin-qiankun'
export default defineConfig((mode) => {
return {
plugins: [
qiankun('subApp', { // 微应用名字,与主应用注册的微应用名字保持一致
useDevMode: true,
}),
],
}
})
- main.ts
import { createApp } from 'vue'
import App from './App.vue'
import {
renderWithQiankun,
qiankunWindow,
QiankunProps,
} from 'vite-plugin-qiankun/dist/helper'
const render = (props: QiankunProps = {}) => {
const { container } = props
const app: string | Element = container?.querySelector('#subApp') || '#subApp' // 避免 id 重复导致微应用挂载失败
createApp(App).mount(app)
}
const initQianKun = () => {
renderWithQiankun({
bootstrap() {
console.log('微应用:bootstrap')
},
mount(props) { // 获取主应用传入数据
console.log('微应用:mount', props)
render(props)
},
unmount(props) {
console.log('微应用:unmount', props)
},
update(props) {
console.log('微应用:update', props)
},
})
}
qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render() // 判断是否使用 qiankun ,保证项目可以独立运行
4 应用间通信
4.1 全局挂载
- props 直接传递
// 主应用在注册微应用时通过props传参
{
name: 'subApp', // 必须与微应用注册名字相同
entry: 'http://localhost:8000', // 入口路径,开发时为微应用所启本地服务,上线时为微应用线上路径
container: '#sub-app-container', // 挂载容器 id
props: {
// 主应用向微应用传递参数
msg: '主应用参数'
}
}
- window 全局挂载
const testFun = () => {}
window[subApp] = { testFun }
4.2 initGlobalState
该 API 虽然还在官方文档中展示,但 qiankun 源代码中已不推荐使用,但未提供新的通信方案
- 主应用
新增 actions.ts 文件
import { initGlobalState, MicroAppStateActions } from 'qiankun'
const state = {
msg: '主应用消息'
}
// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state)
export default actions
- subApp.vue
<template>
<button @click="clickButton">主应用按钮</button>
</template>
<script setup lang="ts">
import actions from '@/actions'
actions.onGlobalStateChange((state, prevState) => {
console.log(state, prevState)
})
const clickButton = () => {
actions.setGlobalState({
msg: '主应用修改主应用消息'
})
}
</script>
- 子应用
main.ts
const initQianKun = () => {
renderWithQiankun({
bootstrap() {
console.log('微应用:bootstrap')
},
mount(props) { // 获取主应用传入数据
console.log('微应用:mount', props)
props.actions.setGlobalState({ msg: '微应用修改主应用消息' })
render(props)
},
unmount(props) {
console.log('微应用:unmount', props)
},
update(props) {
console.log('微应用:update', props)
},
})
}