基于 qiankun 的 Vue 微前端项目

124 阅读4分钟

一、qiankun 核心特性与架构

  • 技术栈无关:主应用和微应用可使用不同框架(Vue/React/Angular 等)
  • 样式隔离:自动隔离各应用 CSS,避免样式污染
  • JS 沙箱:运行时隔离,防止全局变量冲突
  • 预加载:可配置微应用预加载策略,提升用户体验
  • 简单易用:API 简洁,接入成本低

本次实践架构:

  • 主应用(端口 8080):负责微应用的注册、加载和管理
  • 微应用 1(端口 8081):Vue 3 实现的用户管理模块
  • 微应用 2(端口 8082):Vue 3 实现的订单管理模块

二、环境准备

确保环境满足:

  • Node.js ≥ 14.x

  • Vue ≥ 3.x(主应用和微应用)

  • qiankun ≥ 2.0.0

bash

# 安装 qiankun(仅主应用需要)
npm i qiankun -S

三、实战步骤

1. 创建项目结构
# 创建主应用
vue create main-app
cd main-app
vue add router
npm i qiankun -S  # 主应用安装 qiankun
cd ..

# 创建微应用1(用户模块)
vue create user-app
cd user-app
vue add router
cd ..

# 创建微应用2(订单模块)
vue create order-app
cd order-app
vue add router
cd ..
2. 配置微应用(以 user-app 为例)

微应用需要暴露生命周期钩子,供主应用调用。

(1)修改入口文件(main.js)
// user-app/src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

let app = null;

// 渲染函数
function render(props = {}) {
  const { container } = props;
  app = createApp(App);
  app.use(router)
    .mount(container ? container.querySelector('#app') : '#app');
}

// 独立运行时直接渲染
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// 暴露 qiankun 生命周期钩子
export async function bootstrap() {
  console.log('user-app bootstrap');
}

export async function mount(props) {
  console.log('user-app mount', props);
  render(props);  // 传入主应用传递的参数
}

export async function unmount() {
  console.log('user-app unmount');
  app.unmount();  // 卸载应用
  app = null;
}
(2)配置路由(router/index.js)

微应用路由需要添加基础路径,避免与主应用路由冲突:

// user-app/src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import UserList from '../views/UserList.vue'

const routes = [
  {
    path: '/',
    name: 'UserList',
    component: UserList
  }
]

const router = createRouter({
  // 基础路径:独立运行时为 '/', 被嵌入时由主应用传入
  history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/user' : '/'),
  routes
})

export default router
(3)配置 vue.config.js

需要允许跨域并指定入口文件:

// user-app/vue.config.js
module.exports = {
  devServer: {
    port: 8081,
    headers: {
      'Access-Control-Allow-Origin': '*'  // 允许跨域
    }
  },
  configureWebpack: {
    output: {
      // 打包格式必须为 umd
      library: `user-app-[name]`,
      libraryTarget: 'umd',
      chunkLoadingGlobal: `webpackJsonp_user-app`
    }
  }
}
(4)创建页面组件
<!-- user-app/src/views/UserList.vue -->
<template>
  <div class="user-list">
    <h2>用户管理</h2>
    <ul>
      <li v-for="user in users" :key="user.id">
        {{ user.name }} - {{ user.role }}
      </li>
    </ul>
  </div>
</template>

<script setup>
const users = [
  { id: 1, name: '张三', role: '管理员' },
  { id: 2, name: '李四', role: '普通用户' }
]
</script>
3. 配置主应用(main-app)

主应用负责注册和管理微应用。

(1)注册微应用(main.js)
// main-app/src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { registerMicroApps, start } from 'qiankun'

const app = createApp(App)
app.use(router).mount('#app')

// 注册微应用
registerMicroApps([
  {
    name: 'user-app',  // 微应用名称
    entry: '//localhost:8081',  // 微应用入口
    container: '#micro-app-container',  // 挂载容器
    activeRule: '/user',  // 激活路由规则
    props: {  // 传递给微应用的参数
      token: 'main-app-token'
    }
  },
  {
    name: 'order-app',
    entry: '//localhost:8082',
    container: '#micro-app-container',
    activeRule: '/order'
  }
])

// 启动 qiankun
start({
  sandbox: { strictStyleIsolation: true }  // 开启严格样式隔离
})
(2)配置路由(router/index.js)
// main-app/src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/user',  // 对应 user-app 的激活规则
    name: 'User',
    component: () => import('../views/MicroApp.vue')
  },
  {
    path: '/order',  // 对应 order-app 的激活规则
    name: 'Order',
    component: () => import('../views/MicroApp.vue')
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router
(3)创建微应用容器组件
<!-- main-app/src/views/MicroApp.vue -->
<template>
  <!-- 微应用挂载点 -->
  <div id="micro-app-container"></div>
</template>
(4)修改 App.vue 添加导航
<!-- main-app/src/App.vue -->
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link> |
      <router-link to="/user">用户管理</router-link> |
      <router-link to="/order">订单管理</router-link>
    </nav>
    <router-view/>
  </div>
</template>
4. 主应用与微应用通信

qiankun 提供了全局状态管理机制,实现应用间通信。

(1)主应用发送数据
// main-app/src/main.js
import { initGlobalState } from 'qiankun'

// 初始化全局状态
const initialState = {
  userInfo: null
}
const actions = initGlobalState(initialState)

// 监听状态变化
actions.onGlobalStateChange((state, prev) => {
  console.log('主应用监听状态变化:', state, prev)
})

// 主应用更新状态
setTimeout(() => {
  actions.setGlobalState({
    userInfo: { name: 'main-app-user' }
  })
}, 3000)
(2)微应用接收数据
// user-app/src/main.js
export async function mount(props) {
  // 接收主应用传递的通信方法
  props.onGlobalStateChange((state, prev) => {
    console.log('user-app 监听状态变化:', state, prev)
  }, true)

  // 微应用更新状态
  props.setGlobalState({
    userInfo: { name: 'user-app-user' }
  })
  
  render(props);
}
5. 运行效果验证
  1. 分别启动三个应用:

    # 主应用
    cd main-app && npm run serve
    
    # 用户微应用
    cd user-app && npm run serve
    
    # 订单微应用
    cd order-app && npm run serve
    
  2. 访问 http://localhost:8080,可通过导航切换不同微应用,观察应用加载和切换效果。

四、生产环境部署注意事项

  1. 跨域配置:生产环境需在微应用服务器配置 CORS

  2. 静态资源路径:微应用打包时需配置正确的 publicPath

  3. 部署路径:如果微应用部署在非根路径,需同步修改 activeRule 和路由基础路径

  4. 性能优化:配置预加载策略,减少切换时的加载时间:

    // 主应用 main.js
    start({
      prefetch: 'all'  // 预加载所有微应用
    })
    

git地址:gitee.com/xcxsj/vue-q…