Vue 微前端架构:qiankun集成Vue子应用
【摘要】 一、引言1.1 微前端的重要性微前端架构是大型前端应用现代化的核心解决方案,通过qiankun框架实现Vue子应用的集成,解决了单体应用的维护困难、技术栈限制、团队协作等痛点。在企业级应用复杂度不断提升的背景下,微前端提供了可扩展、可维护、技术栈无关的架构方案。1.2 技术价值与市场分析class MicroFrontendAnalysis { /** 微前端市场分析 */ st...
一、引言
1.1 微前端的重要性
1.2 技术价值与市场分析
class MicroFrontendAnalysis {
/** 微前端市场分析 */
static getMarketAnalysis() {
return {
'采用率': '2024年大型企业微前端采用率达65%',
'市场规模': '微前端解决方案市场年增长40%',
'技术需求': '模块化、独立部署、技术栈自由',
'qiankun优势': '生产级成熟度、丰富生态、中文文档完善',
'Vue集成': 'Vue 3组合式API完美适配微前端'
};
}
/** 架构方案对比 */
static getArchitectureComparison() {
return {
'微前端 vs 单体架构': {
'开发效率': '⭐⭐⭐⭐⭐ vs ⭐⭐⭐',
'维护成本': '⭐⭐⭐⭐⭐ vs ⭐⭐',
'技术自由度': '⭐⭐⭐⭐⭐ vs ⭐',
'部署独立性': '⭐⭐⭐⭐⭐ vs ⭐',
'团队协作': '⭐⭐⭐⭐⭐ vs ⭐⭐'
},
'qiankun vs Module Federation': {
'成熟度': '⭐⭐⭐⭐⭐ vs ⭐⭐⭐',
'Vue支持': '⭐⭐⭐⭐⭐ vs ⭐⭐⭐⭐',
'迁移成本': '⭐⭐⭐⭐⭐ vs ⭐⭐',
'社区生态': '⭐⭐⭐⭐ vs ⭐⭐⭐⭐⭐',
'配置复杂度': '⭐⭐⭐⭐ vs ⭐⭐⭐'
},
'qiankun vs Single-SPA': {
'开箱即用': '⭐⭐⭐⭐⭐ vs ⭐⭐⭐',
'沙箱隔离': '⭐⭐⭐⭐⭐ vs ⭐⭐',
'通信机制': '⭐⭐⭐⭐⭐ vs ⭐⭐⭐',
'错误恢复': '⭐⭐⭐⭐ vs ⭐⭐⭐',
'性能优化': '⭐⭐⭐⭐ vs ⭐⭐⭐⭐'
}
};
}
/** 业务价值分析 */
static getBusinessValue() {
return {
'开发效率': '多团队并行开发,交付速度提升60%',
'技术演进': '渐进式技术升级,降低迁移风险',
'故障隔离': '单个应用故障不影响整体系统',
'资源优化': '按需加载,资源利用率提升40%',
'用户体验': '无缝应用切换,操作流畅度提升'
};
}
}
1.3 性能与架构优势
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
二、技术背景
2.1 qiankun微前端架构
graph TB
A[qiankun微前端架构] --> B[主应用层]
A --> C[微应用层]
A --> D[基础设施层]
A --> E[通信层]
B --> B1[应用注册中心]
B --> B2[路由管理]
B --> B3[状态管理]
B --> B4[生命周期管理]
C --> C1[Vue微应用]
C --> C2[React微应用]
C --> C3[Angular微应用]
C --> C4[原生应用]
D --> D1[沙箱隔离]
D --> D2[资源加载]
D --> D3[样式隔离]
D --> D4[错误边界]
E --> E1[全局状态]
E --> E2[事件通信]
E --> E3[共享依赖]
E --> E4[API网关]
B1 --> F[应用调度]
C1 --> F
D1 --> F
F --> G[统一用户体验]
2.2 核心技术栈
// 微前端技术栈配置
export const MicroFrontendTechStack = {
// 主框架
qiankun: {
version: '^2.10.0',
features: [
'基于Single-SPA',
'技术栈无关',
'样式隔离',
'JS沙箱',
'资源预加载',
'应用间通信'
]
},
// Vue微应用支持
vue: {
versions: ['Vue 2.x', 'Vue 3.x'],
integration: {
'路由适配': 'Vue Router集成',
'状态管理': 'Vuex/Pinia支持',
'生命周期': 'qiankun生命周期适配',
'样式方案': 'CSS Modules/Scoped CSS'
}
},
// 通信机制
communication: {
'全局状态': '主应用状态共享',
'自定义事件': '应用间事件通信',
'URL参数': '路由级数据传递',
'本地存储': '跨应用数据持久化'
},
// 部署方案
deployment: {
'独立部署': '每个微应用独立部署',
'CDN加速': '静态资源CDN分发',
'版本管理': '灰度发布、版本回滚',
'监控告警': '应用性能监控'
}
};
三、环境准备与配置
3.1 项目依赖配置
// 主应用 package.json
{
"name": "main-app",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"serve:micro": "serve -s dist -p 8080"
},
"dependencies": {
"vue": "^3.3.0",
"vue-router": "^4.0.0",
"pinia": "^2.0.0",
"qiankun": "^2.10.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^4.0.0"
}
}
// Vue微应用 package.json
{
"name": "vue-micro-app",
"type": "module",
"scripts": {
"dev": "vite --port 3001",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview --port 3001",
"serve:micro": "serve -s dist -p 3001"
},
"dependencies": {
"vue": "^3.3.0",
"vue-router": "^4.0.0",
"pinia": "^2.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"vite": "^4.0.0"
}
}
3.2 qiankun主应用配置
// src/main.js - 主应用入口
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
// 注册微应用
registerMicroApps([
{
name: 'vue-app', // 微应用名称
entry: '//localhost:3001', // 开发环境地址
container: '#micro-container', // 容器ID
activeRule: '/vue', // 激活规则
props: {
routerBase: '/vue', // 路由基础路径
mainStore: app.config.globalProperties.$pinia // 共享状态
}
},
{
name: 'react-app',
entry: '//localhost:3002',
container: '#micro-container',
activeRule: '/react',
props: {
routerBase: '/react'
}
},
{
name: 'angular-app',
entry: '//localhost:3003',
container: '#micro-container',
activeRule: '/angular',
props: {
routerBase: '/angular'
}
}
], {
// 生命周期钩子
beforeLoad: (app) => {
console.log('微应用加载前:', app.name)
return Promise.resolve()
},
beforeMount: (app) => {
console.log('微应用挂载前:', app.name)
return Promise.resolve()
},
afterMount: (app) => {
console.log('微应用挂载后:', app.name)
return Promise.resolve()
},
beforeUnmount: (app) => {
console.log('微应用卸载前:', app.name)
return Promise.resolve()
}
})
// 设置默认微应用
setDefaultMountApp('/vue')
// 启动qiankun
start({
sandbox: {
strictStyleIsolation: true, // 严格的样式隔离
experimentalStyleIsolation: true
},
prefetch: true, // 开启预加载
singular: false // 单实例模式
})
console.log('qiankun微前端框架已启动')
四、核心架构实现
4.1 主应用架构实现
<!-- src/App.vue - 主应用根组件 -->
<template>
<div id="main-app">
<!-- 主应用导航 -->
<header class="main-header">
<nav class="main-nav">
<router-link to="/" class="nav-item">首页</router-link>
<router-link to="/vue" class="nav-item">Vue应用</router-link>
<router-link to="/react" class="nav-item">React应用</router-link>
<router-link to="/angular" class="nav-item">Angular应用</router-link>
</nav>
<!-- 用户信息 -->
<div class="user-info">
<span>欢迎, {{ user.name }}</span>
<button @click="logout">退出</button>
</div>
</header>
<!-- 主应用内容区域 -->
<main class="main-content">
<!-- 主应用路由出口 -->
<router-view v-if="!isMicroAppRoute" />
<!-- 微应用容器 -->
<div
id="micro-container"
v-show="isMicroAppRoute"
class="micro-app-container"
></div>
</main>
<!-- 全局加载状态 -->
<div v-if="loading" class="global-loading">
<div class="loading-spinner"></div>
<span>加载中...</span>
</div>
<!-- 全局错误边界 -->
<div v-if="error" class="global-error">
<h3>应用加载失败</h3>
<p>{{ error.message }}</p>
<button @click="retry">重试</button>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { useUserStore } from '@/stores/user'
const route = useRoute()
const userStore = useUserStore()
// 响应式状态
const loading = ref(false)
const error = ref(null)
const user = ref({ name: '管理员' })
// 计算属性
const isMicroAppRoute = computed(() => {
return route.path.startsWith('/vue') ||
route.path.startsWith('/react') ||
route.path.startsWith('/angular')
})
// 生命周期
onMounted(() => {
// 监听微应用生命周期
window.addEventListener('micro-app-loading', handleMicroAppLoading)
window.addEventListener('micro-app-mounted', handleMicroAppMounted)
window.addEventListener('micro-app-error', handleMicroAppError)
})
onUnmounted(() => {
window.removeEventListener('micro-app-loading', handleMicroAppLoading)
window.removeEventListener('micro-app-mounted', handleMicroAppMounted)
window.removeEventListener('micro-app-error', handleMicroAppError)
})
// 事件处理
const handleMicroAppLoading = (event) => {
loading.value = true
error.value = null
console.log('微应用加载中:', event.detail.appName)
}
const handleMicroAppMounted = (event) => {
loading.value = false
console.log('微应用挂载完成:', event.detail.appName)
}
const handleMicroAppError = (event) => {
loading.value = false
error.value = event.detail.error
console.error('微应用加载失败:', event.detail.error)
}
const logout = () => {
userStore.logout()
// 清理微应用状态
window.location.reload()
}
const retry = () => {
error.value = null
// 重新加载当前路由
window.location.reload()
}
</script>
<style scoped>
.main-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 60px;
background: #001529;
color: white;
}
.main-nav {
display: flex;
gap: 20px;
}
.nav-item {
color: white;
text-decoration: none;
padding: 8px 16px;
border-radius: 4px;
transition: background 0.3s;
}
.nav-item:hover, .nav-item.router-link-active {
background: #1890ff;
}
.micro-app-container {
width: 100%;
height: calc(100vh - 60px);
}
.global-loading {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 9999;
}
.global-error {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 9999;
}
</style>
4.2 Vue微应用适配器
// vue-micro-app/src/public-path.js
// 动态设置publicPath,支持微应用独立运行和嵌入主应用
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
// vue-micro-app/src/main.js - 微应用入口
import './public-path'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { createPinia } from 'pinia'
import App from './App.vue'
import routes from './router'
let router = null
let instance = null
function render(props = {}) {
const { container, routerBase } = props
// 创建路由实例
router = createRouter({
history: createWebHistory(
window.__POWERED_BY_QIANKUN__ ? routerBase : '/'
),
routes
})
// 创建Pinia实例
const pinia = createPinia()
// 创建Vue实例
instance = createApp(App)
instance.use(router)
instance.use(pinia)
// 挂载到指定容器或默认容器
const containerEl = container
? container.querySelector('#app')
: '#app'
instance.mount(containerEl)
}
// 独立运行时直接渲染
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
// 导出qiankun生命周期钩子
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() {
if (instance) {
instance.unmount()
instance._container.innerHTML = ''
instance = null
router = null
}
}
export async function update(props) {
console.log('update props', props)
}
4.3 微应用路由配置
// vue-micro-app/src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue'),
meta: { title: '首页' }
},
{
path: '/about',
name: 'About',
component: () => import('@/views/About.vue'),
meta: { title: '关于我们' }
},
{
path: '/user',
name: 'User',
component: () => import('@/views/User.vue'),
meta: { title: '用户管理', requireAuth: true }
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/404.vue'),
meta: { title: '页面不存在' }
}
]
const router = createRouter({
history: createWebHistory(
window.__POWERED_BY_QIANKUN__ ? '/vue' : '/'
),
routes
})
// 路由守卫
router.beforeEach((to, from, next) => {
// 设置页面标题
if (to.meta.title) {
document.title = to.meta.title
}
// 认证检查
if (to.meta.requireAuth) {
const token = localStorage.getItem('token')
if (!token) {
next('/login')
return
}
}
next()
})
export default routes
4.4 应用间通信机制
// src/utils/micro-communication.js
class MicroFrontendCommunication {
constructor() {
this.eventListeners = new Map()
this.sharedState = new Map()
}
// 初始化通信
init() {
// 监听主应用消息
window.addEventListener('micro-frontend-message', this.handleMessage.bind(this))
}
// 发送消息到主应用或其他微应用
sendMessage(type, payload, target = 'main') {
const message = {
type,
payload,
source: window.__POWERED_BY_QIANKUN__ ? window.__QIANKUN_APP_NAME__ : 'main',
timestamp: Date.now()
}
const event = new CustomEvent('micro-frontend-message', {
detail: message,
bubbles: true
})
window.dispatchEvent(event)
console.log(`[通信] 发送消息: ${type}`, message)
}
// 处理接收到的消息
handleMessage(event) {
const { type, payload, source } = event.detail
// 避免处理自己发送的消息
if (source === (window.__POWERED_BY_QIANKUN__ ? window.__QIANKUN_APP_NAME__ : 'main')) {
return
}
console.log(`[通信] 接收消息: ${type}`, event.detail)
// 触发对应的监听器
if (this.eventListeners.has(type)) {
this.eventListeners.get(type).forEach(callback => {
try {
callback(payload, source)
} catch (error) {
console.error(`[通信] 消息处理错误: ${type}`, error)
}
})
}
}
// 注册消息监听器
on(messageType, callback) {
if (!this.eventListeners.has(messageType)) {
this.eventListeners.set(messageType, [])
}
this.eventListeners.get(messageType).push(callback)
// 返回取消监听函数
return () => {
const listeners = this.eventListeners.get(messageType)
const index = listeners.indexOf(callback)
if (index > -1) {
listeners.splice(index, 1)
}
}
}
// 设置共享状态
setState(key, value) {
this.sharedState.set(key, value)
this.sendMessage('state-update', { key, value })
}
// 获取共享状态
getState(key) {
return this.sharedState.get(key)
}
}
// 创建全局通信实例
const microCommunication = new MicroFrontendCommunication()
microCommunication.init()
export default microCommunication
五、实际应用场景
5.1 用户认证共享场景
// src/utils/auth-shared.js
// 主应用认证管理
class AuthManager {
constructor() {
this.token = localStorage.getItem('auth-token')
this.userInfo = JSON.parse(localStorage.getItem('user-info') || 'null')
this.listeners = new Set()
}
// 登录
async login(credentials) {
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
if (response.ok) {
const data = await response.json()
this.setAuthData(data.token, data.user)
this.notifyListeners('login', data.user)
return data
} else {
throw new Error('登录失败')
}
} catch (error) {
console.error('登录错误:', error)
throw error
}
}
// 设置认证数据
setAuthData(token, userInfo) {
this.token = token
this.userInfo = userInfo
localStorage.setItem('auth-token', token)
localStorage.setItem('user-info', JSON.stringify(userInfo))
// 通知所有微应用
microCommunication.sendMessage('auth-changed', {
type: 'login',
user: userInfo
})
}
// 登出
logout() {
this.token = null
this.userInfo = null
localStorage.removeItem('auth-token')
localStorage.removeItem('user-info')
this.notifyListeners('logout')
microCommunication.sendMessage('auth-changed', { type: 'logout' })
}
// 添加监听器
addListener(callback) {
this.listeners.add(callback)
return () => this.listeners.delete(callback)
}
// 通知监听器
notifyListeners(event, data) {
this.listeners.forEach(callback => {
try {
callback(event, data)
} catch (error) {
console.error('认证监听器错误:', error)
}
})
}
// 检查认证状态
isAuthenticated() {
return !!this.token
}
}
// 创建全局认证管理器
export const authManager = new AuthManager()
// 微应用中的认证Hook
export function useAuth() {
const isAuthenticated = ref(authManager.isAuthenticated())
const userInfo = ref(authManager.userInfo)
const updateAuthState = (event, data) => {
if (event === 'login') {
isAuthenticated.value = true
userInfo.value = data
} else if (event === 'logout') {
isAuthenticated.value = false
userInfo.value = null
}
}
onMounted(() => {
// 监听认证状态变化
const unsubscribe = authManager.addListener(updateAuthState)
// 监听主应用认证消息
const removeMessageListener = microCommunication.on('auth-changed', (payload) => {
updateAuthState(payload.type, payload.user)
})
onUnmounted(() => {
unsubscribe()
removeMessageListener()
})
})
return {
isAuthenticated,
userInfo,
login: authManager.login.bind(authManager),
logout: authManager.logout.bind(authManager)
}
}
5.2 数据共享和状态管理
// src/stores/global-store.js
import { defineStore } from 'pinia'
export const useGlobalStore = defineStore('global', {
state: () => ({
// 用户信息
user: null,
// 应用配置
config: {
theme: 'light',
language: 'zh-CN',
permissions: []
},
// 共享数据
sharedData: new Map(),
// 微应用状态
microApps: new Map()
}),
actions: {
// 设置用户信息
setUser(userInfo) {
this.user = userInfo
this.notifyMicroApps('user-changed', userInfo)
},
// 更新配置
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig }
this.notifyMicroApps('config-changed', this.config)
},
// 设置共享数据
setSharedData(key, value) {
this.sharedData.set(key, value)
this.notifyMicroApps('data-changed', { key, value })
},
// 获取共享数据
getSharedData(key) {
return this.sharedData.get(key)
},
// 注册微应用状态
registerMicroApp(appName, state) {
this.microApps.set(appName, state)
},
// 通知所有微应用
notifyMicroApps(event, data) {
microCommunication.sendMessage(event, data)
},
// 监听微应用消息
onMicroAppMessage(event, callback) {
return microCommunication.on(event, callback)
}
}
})
// 在微应用中使用
export function useGlobalStoreInMicro() {
const globalStore = useGlobalStore()
// 监听主应用状态变化
onMounted(() => {
const removers = []
removers.push(microCommunication.on('user-changed', (user) => {
globalStore.setUser(user)
}))
removers.push(microCommunication.on('config-changed', (config) => {
globalStore.updateConfig(config)
}))
onUnmounted(() => {
removers.forEach(remove => remove())
})
})
return globalStore
}
六、高级特性实现
6.1 动态应用加载
// src/utils/dynamic-loading.js
import { loadMicroApp } from 'qiankun'
class DynamicAppManager {
constructor() {
this.loadedApps = new Map()
this.appConfigs = new Map()
}
// 注册应用配置
registerApp(config) {
this.appConfigs.set(config.name, config)
}
// 动态加载应用
async loadApp(appName, container, props = {}) {
if (this.loadedApps.has(appName)) {
console.warn(`应用 ${appName} 已加载`)
return this.loadedApps.get(appName)
}
const config = this.appConfigs.get(appName)
if (!config) {
throw new Error(`应用 ${appName} 未注册`)
}
try {
const microApp = loadMicroApp({
name: appName,
entry: config.entry,
container: container,
props: {
...config.props,
...props
}
}, {
sandbox: {
strictStyleIsolation: true
}
})
this.loadedApps.set(appName, microApp)
console.log(`应用 ${appName} 加载成功`)
return microApp
} catch (error) {
console.error(`应用 ${appName} 加载失败:`, error)
throw error
}
}
// 卸载应用
async unmountApp(appName) {
const microApp = this.loadedApps.get(appName)
if (microApp) {
await microApp.unmount()
this.loadedApps.delete(appName)
console.log(`应用 ${appName} 已卸载`)
}
}
// 获取应用状态
getAppStatus(appName) {
const microApp = this.loadedApps.get(appName)
return microApp ? microApp.getStatus() : 'NOT_LOADED'
}
// 预加载应用
prefetchApp(appName) {
const config = this.appConfigs.get(appName)
if (config && typeof window !== 'undefined') {
// 使用qiankun的预加载功能
import('qiankun').then(({ prefetchApps }) => {
prefetchApps([{
name: appName,
entry: config.entry
}])
})
}
}
}
// 创建全局动态应用管理器
export const dynamicAppManager = new DynamicAppManager()
// 注册示例应用
dynamicAppManager.registerApp({
name: 'vue-admin',
entry: '//localhost:3001',
props: {
routerBase: '/admin'
}
})
dynamicAppManager.registerApp({
name: 'vue-dashboard',
entry: '//localhost:3002',
props: {
routerBase: '/dashboard'
}
})
6.2 错误边界和降级处理
<!-- src/components/MicroAppErrorBoundary.vue -->
<template>
<div class="error-boundary">
<div v-if="hasError" class="error-fallback">
<h3>微应用加载失败</h3>
<p>{{ errorMessage }}</p>
<div class="error-actions">
<button @click="handleRetry" class="retry-btn">重试加载</button>
<button @click="handleReset" class="reset-btn">重置应用</button>
<button @click="handleReport" class="report-btn">报告问题</button>
</div>
<div v-if="errorDetails" class="error-details">
<details>
<summary>错误详情</summary>
<pre>{{ errorDetails }}</pre>
</details>
</div>
</div>
<div v-else class="micro-app-container">
<slot></slot>
</div>
</div>
</template>
<script setup>
import { ref, onErrorCaptured, defineEmits } from 'vue'
const emits = defineEmits(['retry', 'reset', 'report'])
const hasError = ref(false)
const errorMessage = ref('')
const errorDetails = ref('')
// 捕获子组件错误
onErrorCaptured((error, instance, info) => {
console.error('微应用错误:', error)
hasError.value = true
errorMessage.value = getFriendlyErrorMessage(error)
errorDetails.value = `${error.toString()}\n\n组件: ${instance?.$options.name || '未知'}\n位置: ${info}`
// 阻止错误继续向上传播
return false
})
// 友好错误消息
const getFriendlyErrorMessage = (error) => {
if (error.message.includes('Loading chunk')) {
return '应用资源加载失败,请检查网络连接'
} else if (error.message.includes('Unexpected token')) {
return '应用脚本解析错误,可能是版本不兼容'
} else if (error.message.includes('Timeout')) {
return '应用加载超时,请重试'
} else {
return '应用加载过程中发生错误'
}
}
// 重试加载
const handleRetry = () => {
hasError.value = false
errorMessage.value = ''
errorDetails.value = ''
emits('retry')
}
// 重置应用
const handleReset = () => {
// 清理应用状态
if (window.__POWERED_BY_QIANKUN__) {
// 通知主应用重置微应用
window.dispatchEvent(new CustomEvent('micro-app-reset', {
detail: { appName: window.__QIANKUN_APP_NAME__ }
}))
}
emits('reset')
}
// 报告问题
const handleReport = () => {
const reportData = {
app: window.__QIANKUN_APP_NAME__ || 'unknown',
url: window.location.href,
error: errorDetails.value,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
}
// 发送错误报告
fetch('/api/error-report', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(reportData)
}).then(() => {
alert('问题报告已提交')
}).catch(() => {
alert('报告提交失败,请稍后重试')
})
emits('report', reportData)
}
</script>
<style scoped>
.error-boundary {
width: 100%;
height: 100%;
}
.error-fallback {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
padding: 20px;
text-align: center;
background: #f5f5f5;
border-radius: 8px;
}
.error-actions {
margin: 20px 0;
display: flex;
gap: 10px;
}
.retry-btn, .reset-btn, .report-btn {
padding: 8px 16px;
border: 1px solid #d9d9d9;
border-radius: 4px;
background: white;
cursor: pointer;
transition: all 0.3s;
}
.retry-btn:hover {
background: #1890ff;
color: white;
}
.reset-btn:hover {
background: #52c41a;
color: white;
}
.report-btn:hover {
background: #faad14;
color: white;
}
.error-details {
margin-top: 20px;
max-width: 600px;
}
.error-details details {
text-align: left;
}
.error-details pre {
background: #f0f0f0;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
font-size: 12px;
}
</style>
七、部署和运维
7.1 Docker容器化部署
# 主应用Dockerfile
FROM nginx:alpine
# 安装Node.js用于构建
RUN apk add --no-cache nodejs npm
# 设置工作目录
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源码
COPY . .
# 构建应用
RUN npm run build
# 复制nginx配置
COPY nginx.conf /etc/nginx/nginx.conf
# 复制构建结果到nginx目录
RUN cp -r dist/* /usr/share/nginx/html/
# 暴露端口
EXPOSE 80
# 启动nginx
CMD ["nginx", "-g", "daemon off;"]
# nginx.conf
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 主应用服务器
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 主应用路由
location / {
try_files $uri $uri/ /index.html;
}
# 微应用路由代理
location ~ ^/(vue|react|angular) {
try_files $uri $uri/ /index.html;
}
# API代理
location /api/ {
proxy_pass http://api-server:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 微应用静态资源
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
}
7.2 环境配置管理
// config/environments.js
export const environments = {
development: {
// 开发环境
mainApp: {
url: 'http://localhost:8080',
microApps: {
vue: 'http://localhost:3001',
react: 'http://localhost:3002',
angular: 'http://localhost:3003'
}
},
api: {
baseURL: 'http://localhost:3000/api'
}
},
staging: {
// 预发布环境
mainApp: {
url: 'https://staging.example.com',
microApps: {
vue: 'https://vue.staging.example.com',
react: 'https://react.staging.example.com',
angular: 'https://angular.staging.example.com'
}
},
api: {
baseURL: 'https://api.staging.example.com'
}
},
production: {
// 生产环境
mainApp: {
url: 'https://example.com',
microApps: {
vue: 'https://vue.example.com',
react: 'https://react.example.com',
angular: 'https://angular.example.com'
}
},
api: {
baseURL: 'https://api.example.com'
}
}
}
// 根据环境变量获取配置
export const getConfig = () => {
const env = process.env.NODE_ENV || 'development'
return environments[env]
}
// 微应用配置生成
export const generateMicroAppConfig = (env = 'development') => {
const config = environments[env]
return [
{
name: 'vue-app',
entry: config.mainApp.microApps.vue,
container: '#micro-container',
activeRule: '/vue',
props: {
routerBase: '/vue',
apiBaseURL: config.api.baseURL
}
},
{
name: 'react-app',
entry: config.mainApp.microApps.react,
container: '#micro-container',
activeRule: '/react',
props: {
routerBase: '/react',
apiBaseURL: config.api.baseURL
}
},
{
name: 'angular-app',
entry: config.mainApp.microApps.angular,
container: '#micro-container',
activeRule: '/angular',
props: {
routerBase: '/angular',
apiBaseURL: config.api.baseURL
}
}
]
}
八、测试策略
8.1 微前端测试配置
// 主应用测试配置
// vitest.config.main.js
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'happy-dom',
globals: true,
setupFiles: ['./tests/setup.js'],
coverage: {
reporter: ['text', 'html', 'lcov'],
exclude: [
'**/micro-apps/**',
'**/node_modules/**',
'**/dist/**'
]
}
}
})
// 微应用测试配置
// vue-micro-app/vitest.config.js
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'happy-dom',
globals: true,
setupFiles: ['./tests/setup.js'],
// 模拟qiankun环境
environmentOptions: {
qiankun: {
__POWERED_BY_QIANKUN__: true,
__INJECTED_PUBLIC_PATH_BY_QIANKUN__: '/vue/'
}
}
}
})
8.2 集成测试示例
// tests/e2e/micro-frontend.spec.js
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { startApp, stopApp } from './utils/app-manager'
describe('微前端集成测试', () => {
let mainApp
let vueApp
beforeAll(async () => {
// 启动主应用和微应用
mainApp = await startApp('main')
vueApp = await startApp('vue')
})
afterAll(async () => {
await stopApp('main')
await stopApp('vue')
})
it('应该正确加载主应用', async () => {
await mainApp.page.goto('http://localhost:8080')
const title = await mainApp.page.title()
expect(title).toContain('主应用')
// 验证导航菜单
const navItems = await mainApp.page.$$('.nav-item')
expect(navItems).toHaveLength(4)
})
it('应该能够切换到Vue微应用', async () => {
await mainApp.page.click('a[href="#/vue"]')
// 等待微应用加载
await mainApp.page.waitForSelector('#micro-container .vue-app')
// 验证微应用内容
const vueContent = await mainApp.page.$eval(
'#micro-container',
el => el.textContent
)
expect(vueContent).toContain('Vue应用')
})
it('应该保持认证状态同步', async () => {
// 在主应用登录
await mainApp.page.click('.login-btn')
await mainApp.page.fill('#username', 'testuser')
await mainApp.page.fill('#password', 'password')
await mainApp.page.click('#login-submit')
// 切换到微应用验证登录状态
await mainApp.page.click('a[href="#/vue"]')
await mainApp.page.waitForSelector('#micro-container .user-info')
const userInfo = await mainApp.page.$eval(
'#micro-container .user-info',
el => el.textContent
)
expect(userInfo).toContain('testuser')
})
it('应该处理应用间通信', async () => {
// 在微应用中触发事件
await mainApp.page.click('#micro-container .send-message')
// 验证主应用收到消息
const message = await mainApp.page.waitForSelector('.notification')
expect(message).toBeTruthy()
})
})
九、总结
9.1 技术成果总结
核心架构特性
- •
应用隔离:完整的JS沙箱和样式隔离机制 - •
独立开发:各微应用可独立开发、测试、部署 - •
技术栈无关:支持Vue、React、Angular等任意技术栈 - •
通信机制:灵活的应用间数据交换和状态同步 - •
动态加载:按需加载,资源优化
性能与运维指标
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9.2 最佳实践总结
架构设计原则
class MicroFrontendBestPractices {
static getArchitecturePrinciples() {
return {
'单一职责': '每个微应用负责明确的业务领域',
'自治性': '微应用可独立开发、测试、部署',
'技术中立': '不限制微应用的技术栈选择',
'契约驱动': '通过明确定义的接口进行通信',
'容错设计': '单个应用故障不影响整体系统'
};
}
static getDevelopmentPractices() {
return {
'标准化': '统一的代码规范、目录结构、构建流程',
'文档化': '完善的API文档、部署文档、运维手册',
'自动化': 'CI/CD流水线、自动化测试、监控告警',
'监控化': '性能监控、错误追踪、使用分析',
'安全化': '权限控制、数据加密、安全审计'
};
}
}
9.3 未来展望
技术演进趋势
class MicroFrontendFuture {
static getTechnologyTrends() {
return {
'2024': [
'Module Federation成熟',
'Web Components集成',
'边缘计算部署',
'AI驱动的资源优化'
],
'2025': [
'Serverless微前端',
'量子安全通信',
'元宇宙集成',
'自适应微应用'
]
};
}
static getIndustryStandards() {
return {
'微前端架构规范': '行业标准化的微前端架构模式',
'通信协议标准': '统一的应用间通信协议',
'安全标准': '微前端安全最佳实践',
'性能指标': '微前端性能评估标准'
};
}
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)