Vue 微前端架构:qiankun集成Vue子应用

举报
William 发表于 2025/11/12 10:10:45 2025/11/12
【摘要】 一、引言1.1 微前端的重要性微前端架构是大型前端应用现代化的核心解决方案,通过qiankun框架实现Vue子应用的集成,解决了单体应用的维护困难、技术栈限制、团队协作等痛点。在企业级应用复杂度不断提升的背景下,微前端提供了可扩展、可维护、技术栈无关的架构方案。1.2 技术价值与市场分析class MicroFrontendAnalysis { /** 微前端市场分析 */ st...


一、引言

1.1 微前端的重要性

微前端架构大型前端应用现代化的核心解决方案,通过qiankun框架实现Vue子应用的集成,解决了单体应用维护困难技术栈限制团队协作等痛点。在企业级应用复杂度不断提升的背景下,微前端提供了可扩展、可维护、技术栈无关的架构方案。

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 性能与架构优势

指标
单体架构
传统微服务
微前端架构
优势分析
构建时间
5-15分钟
3-8分钟
1-3分钟
独立构建
部署频率
周/月
天/周
小时/天
独立部署
团队规模
10-20人
5-10人/团队
无限制
团队自治
技术栈
单一技术栈
有限选择
任意技术栈
技术自由
故障影响
全局影响
服务影响
应用级隔离
故障隔离

二、技术背景

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 技术成果总结

Vue + qiankun微前端架构实现了现代化前端应用的完整解决方案,主要成果包括:

核心架构特性

  • 应用隔离:完整的JS沙箱和样式隔离机制
  • 独立开发:各微应用可独立开发、测试、部署
  • 技术栈无关:支持Vue、React、Angular等任意技术栈
  • 通信机制:灵活的应用间数据交换和状态同步
  • 动态加载:按需加载,资源优化

性能与运维指标

特性
传统单体应用
微前端架构
改进效果
构建时间
5-15分钟
1-3分钟
提升80%
部署频率
周级部署
天级部署
频率提升7倍
团队规模
有限制
无限制
扩展性无限
技术演进
困难
平滑迁移
风险降低70%
故障影响
全局影响
应用级隔离
影响范围缩小90%

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 {
            '微前端架构规范': '行业标准化的微前端架构模式',
            '通信协议标准': '统一的应用间通信协议',
            '安全标准': '微前端安全最佳实践',
            '性能指标': '微前端性能评估标准'
        };
    }
}
Vue + qiankun微前端架构通过技术创新工程实践,为大型前端应用提供了可扩展、可维护、高性能的解决方案,推动了前端架构的现代化演进。随着技术生态的不断完善,微前端将在企业级应用开发中发挥越来越重要的作用
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。