企业级微前端项目完整创建步骤,第三步:配置 Vue 子应用

3.1 安装依赖

javascript 复制代码
cd ../vue-app
npm install vite-plugin-qiankun

3.2 配置 Vite vite.config.js

javascript 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 引入 qiankun 插件,用于支持 qiankun 微前端
import qiankun from 'vite-plugin-qiankun'

export default defineConfig({
  plugins: [
    vue(),
    // 配置 qiankun 插件
    // 参数说明:
    // - 'vue-app': 子应用名称,必须与主应用注册时的 name 一致
    // - useDevMode: 开发模式,默认为 false,开发时设为 true
    qiankun('vue-app', { useDevMode: true }),
  ],
  server: {
    port: 8081,  // 子应用端口,必须与主应用注册时的 entry 端口一致
    headers: {
      // 允许跨域,主应用需要加载子应用的资源
      'Access-Control-Allow-Origin': '*',
    },
  },
})

3.3 创建子应用入口 src/main.ts

javascript 复制代码
/**
 * Vue 子应用入口文件
 * 负责:导出 qiankun 生命周期钩子、渲染应用
 */
import { createApp, h } from 'vue'
import App from './App.vue'
import './style.css'

// 保存 Vue 应用实例,用于卸载时清理
let app: ReturnType<typeof createApp> | null = null

/**
 * qiankun 生命周期钩子:bootstrap
 * 在子应用第一次加载时调用,只会调用一次
 */
export async function bootstrap() {
  console.log('[Vue子应用] bootstrap 生命周期')
}

/**
 * qiankun 生命周期钩子:mount
 * 在子应用挂载时调用
 * @param props qiankun 传递的参数
 */
export async function mount(props: any) {
  console.log('[Vue子应用] mount 生命周期', props)
  
  // 获取挂载容器
  // 如果在 qiankun 环境下,使用 props.container
  // 如果独立运行,使用 document.getElementById('app')
  const mountTarget = props?.container 
    ? props.container.querySelector('#vue-app-root') || props.container
    : document.getElementById('app')
  
  // 确保挂载容器存在
  if (!mountTarget) {
    console.error('[Vue子应用] 挂载容器不存在')
    return
  }
  
  // 在 qiankun 环境下,清空容器内容,避免重复挂载
  if (props?.container) {
    props.container.innerHTML = '<div id="vue-app-root"></div>'
  }
  
  // 创建并挂载 Vue 应用
  app = createApp({
    // 使用 h 函数渲染根组件
    render: () => h(App),
  })
  
  // 挂载到目标容器
  app.mount(mountTarget)
  
  console.log('[Vue子应用] 已挂载')
}

/**
 * qiankun 生命周期钩子:unmount
 * 在子应用卸载时调用
 */
export async function unmount() {
  console.log('[Vue子应用] unmount 生命周期')
  
  // 卸载 Vue 应用,避免内存泄漏
  if (app) {
    app.unmount()
    app = null
  }
  
  console.log('[Vue子应用] 已卸载')
}

/**
 * 如果不是在 qiankun 环境下,独立运行子应用
 */
if (!(window as any).__POWERED_BY_QIANKUN__) {
  // 直接创建并挂载应用
  createApp(App).mount('#app')
}

3.4 创建 Vue 子应用组件 src/App.vue

javascript 复制代码
<template>
  <div class="vue-app">
    <h2>🟢 Vue 3 子应用</h2>
    <p>这是一个基于 Vue 3 的微前端子应用</p>
    
    <!-- 导航按钮 -->
    <div class="navigation">
      <button @click="goToMain">🏠 返回主应用</button>
      <button @click="goToReact">🎯 跳转到 React 子应用</button>
    </div>
    
    <!-- 全局状态展示 -->
    <div class="state-panel">
      <h3>📊 全局状态(来自主应用)</h3>
      <div class="state-item">
        <span class="label">当前用户:</span>
        <span class="value">{{ globalState.user?.name || '未登录' }}</span>
      </div>
      <div class="state-item">
        <span class="label">用户角色:</span>
        <span class="value">{{ globalState.user?.role || '普通用户' }}</span>
      </div>
      <div class="state-item">
        <span class="label">计数器:</span>
        <span class="value highlight">{{ globalState.count }}</span>
      </div>
      <div class="state-item">
        <span class="label">子应用消息:</span>
        <span class="value">{{ globalState.message || '暂无消息' }}</span>
      </div>
    </div>

    <!-- 子应用操作区 -->
    <div class="action-panel">
      <h3>🎮 Vue 子应用操作(通信演示)</h3>
      
      <div class="action-item">
        <label>发送消息给其他应用:</label>
        <input 
          v-model="message" 
          placeholder="输入消息" 
        />
        <button @click="sendMessage">发送消息</button>
      </div>

      <div class="action-buttons">
        <button @click="incrementCount">增加计数器(同步到主应用)</button>
        <button @click="resetCount">重置计数器</button>
        <button @click="showNotify">调用主应用通知</button>
      </div>
    </div>
    
    <div class="features">
      <div class="feature-card">
        <h3>📦 技术栈</h3>
        <ul>
          <li>Vue 3 + Composition API</li>
          <li>Vite 构建工具</li>
          <li>TypeScript 支持</li>
        </ul>
      </div>
      
      <div class="feature-card">
        <h3>🔧 特性</h3>
        <ul>
          <li>独立开发和部署</li>
          <li>样式隔离</li>
          <li>全局状态共享</li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'

/**
 * 从 window 对象获取主应用的 actions
 * 解决 props 传递失败的问题
 */
const getActions = () => {
  return (window as any).__MICRO_APP_ACTIONS__
}

const actions = getActions()
console.log('[Vue子应用] actions:', actions)

// 全局状态
const globalState = ref(actions?.getGlobalState?.() || {})
const message = ref('')
let unsubscribe: (() => void) | undefined

/**
 * 返回主应用
 */
const goToMain = () => {
  if (actions?.push) {
    actions.push('/')
  } else {
    window.location.href = '/'
  }
}

/**
 * 跳转到 React 子应用
 */
const goToReact = () => {
  if (actions?.push) {
    actions.push('/react-app')
  } else {
    window.location.href = '/react-app'
  }
}

/**
 * 发送消息给其他应用
 */
const sendMessage = () => {
  if (actions?.setGlobalState && message.value) {
    actions.setGlobalState({ message: `[Vue] ${message.value}` })
    actions.showNotification('消息已发送', 'success')
    message.value = ''
  }
}

/**
 * 增加计数器
 */
const incrementCount = () => {
  if (actions?.incrementCount) {
    actions.incrementCount()
  }
}

/**
 * 重置计数器
 */
const resetCount = () => {
  if (actions?.resetCount) {
    actions.resetCount()
  }
}

/**
 * 调用主应用通知
 */
const showNotify = () => {
  if (actions?.showNotification) {
    actions.showNotification('这是 Vue 子应用发起的通知!', 'info')
  }
}

onMounted(() => {
  console.log('[Vue子应用] 组件已挂载')
  
  // 初始化全局状态
  if (actions?.getGlobalState) {
    globalState.value = actions.getGlobalState()
  }
  
  // 订阅全局状态变更
  if (actions?.onGlobalStateChange) {
    unsubscribe = actions.onGlobalStateChange((state) => {
      globalState.value = state
    })
  }
})

onUnmounted(() => {
  console.log('[Vue子应用] 组件已卸载')
  // 取消订阅,避免内存泄漏
  if (unsubscribe) {
    unsubscribe()
  }
})
</script>

<style scoped>
.vue-app {
  padding: 24px;
}

.navigation {
  display: flex;
  gap: 12px;
  margin-bottom: 24px;
}

.navigation button {
  padding: 10px 20px;
  background: #1890ff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.navigation button:hover {
  background: #40a9ff;
}

.state-panel, .action-panel {
  background: #f5f5f5;
  border-radius: 8px;
  padding: 24px;
  margin-bottom: 24px;
}

.state-item {
  display: flex;
  margin-bottom: 12px;
}

.label {
  font-weight: bold;
  width: 120px;
}

.value {
  color: #1890ff;
}

.value.highlight {
  font-size: 18px;
  font-weight: bold;
}

.action-item {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
}

.action-item input {
  flex: 1;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.action-item button {
  padding: 8px 16px;
  background: #1890ff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.action-buttons {
  display: flex;
  gap: 12px;
}

.action-buttons button {
  padding: 10px 20px;
  background: #52c41a;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.action-buttons button:hover {
  background: #73d13d;
}

.features {
  display: flex;
  gap: 24px;
}

.feature-card {
  flex: 1;
  background: #f5f5f5;
  border-radius: 8px;
  padding: 24px;
}

.feature-card ul {
  list-style: none;
  padding: 0;
}

.feature-card li {
  padding: 8px 0;
  border-bottom: 1px solid #ddd;
}
</style>
相关推荐
自强的小白1 小时前
条件查询或查询全部时时前端请求数据等时间格式
前端
阳光是sunny1 小时前
LangGraph实战教程:一文搞懂图的状态(State)管理
前端·人工智能·后端
whyfail2 小时前
前端学 Spring Boot(5):从“你是谁”到“服务真的上线了”
前端·spring boot·后端
程序员黑豆2 小时前
鸿蒙开发:RelativeContainer相对布局组件实战——实现5个文本组件对齐
前端·华为·harmonyos
环境栈笔记2 小时前
高性价比指纹浏览器推荐与选型:如何对照价格和实际可用功能筛选候选
前端·人工智能·后端·自动化
hyf3266333 小时前
泛程序哪有想象中难!小白跟着走一遍就全懂
前端·爬虫·搜索引擎·seo·蜘蛛池
码农学院3 小时前
React公众号开发:母婴用品会员积分体系技术方案
前端·react.js·前端框架
路光.3 小时前
Vue2升级Vue3处理原 UMD/CommonJS 打包文件,不是标准 ESM
前端·javascript·vue.js·vue3
黄华SJ520it3 小时前
门店预约系统开发:提升服务行业数字化转型
前端·数据库·系统开发