微前端(qiankun)使用教程

一、什么是微前端

微前端(Micro-Frontends)是一种架构模式,它将前端应用分解为一系列可独立开发、测试、部署的小型应用(微应用),并能将这些微应用组合成一个完整的应用。其核心思想借鉴了后端微服务架构,旨在解决大型前端应用在长期维护中出现的代码臃肿、团队协作低效、技术栈锁定等问题。

  • 技术栈无关:各微应用可采用不同技术栈(如 React、Vue、Angular 等)
  • 独立开发部署:各团队可独立开发、测试、发布自己的微应用,不影响整体
  • 增量升级:无需一次性重构整个应用,可逐步替换旧系统
  • 团队自治:不同团队负责不同微应用,降低协作成本
  • 复用与共享:公共组件和逻辑可抽离为共享依赖

二、微前端和iframe对比

1. 微前端(Micro-Frontends)
  • 本质:将一个大型前端应用拆分为多个独立开发、部署的 “微应用”,通过容器应用(Host)实现微应用的加载、卸载、路由分发和通信,最终组合成一个完整应用。
  • 技术核心:基于路由匹配或手动触发加载微应用资源(JS/CSS/HTML),通过沙箱隔离(JS/CSS)、全局状态管理或事件总线实现通信,微应用与容器共享同一个浏览器上下文(window、DOM 根节点等)。
2. iframe
  • 本质:通过 HTML 原生标签 <iframe> 嵌入另一个完整的 HTML 文档,形成一个 “页面中的页面”。
  • 技术核心:每个 iframe 拥有独立的浏览器上下文(独立的 windowdocument、JS 执行环境和 DOM 树),与父页面通过 postMessagewindow.parent 进行有限通信。
3. 关键区别对比
对比维度 微前端 iframe
上下文隔离 共享同一个 window 上下文,通过沙箱(如 Proxy)隔离全局变量 完全隔离,每个 iframe 有独立 window 和 DOM 树
样式隔离 需要手动实现(如 CSS Modules、Shadow DOM、BEM 命名) 天然隔离(iframe 内部样式不影响父页面,反之亦然)
通信方式 灵活多样:全局事件总线、共享状态库(Redux/Vuex)、Props 传递 仅支持 postMessagewindow.parent 跨域通信,有格式限制
路由同步 容器应用统一管理路由,微应用可嵌套子路由,路由状态全局共享 父页面与 iframe 路由独立,同步需手动监听和同步(复杂)
性能开销 初始加载成本低(共享公共依赖如 React/Vue),运行时性能接近单应用 每个 iframe 需独立加载完整资源(重复加载框架库),性能开销大
用户体验 微应用切换流畅(无刷新),与单应用体验一致 切换 iframe 可能有白屏或卡顿(资源重新加载),体验割裂
技术栈灵活性 支持多技术栈(React、Vue、Angular 等)混合开发 同样支持多技术栈,但整合度低(每个 iframe 是独立应用)
开发 / 部署成本 较高(需设计架构、处理隔离和通信),但长期维护成本低 极低(原生标签直接使用),但复杂场景下维护成本高
应用整合度 高(微应用融入容器,视觉和交互统一) 低(iframe 是独立窗口,样式和交互易割裂)

4. 优缺点分析

1. 微前端
  • 优点:
    • 体验接近单应用(无刷新切换、路由统一)。
    • 共享公共依赖,减少资源重复加载,性能更优。
    • 通信灵活,可实现复杂数据交互。
    • 样式和逻辑隔离可控,既避免冲突又能共享全局样式。
    • 支持增量升级和独立部署,适合大型团队协作。
  • 缺点:
    • 架构设计复杂(需处理加载、隔离、通信等问题)。
    • 需引入框架(如 qiankun、single-spa)或自定义实现,学习成本高。
    • 沙箱隔离可能存在边缘场景兼容问题。
2. iframe
  • 优点:
    • 实现简单(一行标签即可嵌入应用)。
    • 完全隔离,避免任何 JS/CSS 冲突,适合整合第三方应用。
    • 无需额外框架,原生支持,开发成本极低。
  • 缺点:
    • 性能差(资源重复加载、浏览器渲染开销大)。
    • 用户体验割裂(路由不同步、切换有延迟)。
    • 通信繁琐(仅 postMessage 支持跨域,数据格式需手动定义)。
    • 样式统一困难(iframe 内部样式难以被父页面控制)。
    • 对 SEO 不友好(搜索引擎难以抓取 iframe 内容)。

三、通过 qiankun实现一个微前端

一、环境准备

需要准备 1 个容器应用(主应用)至少 1 个微应用,技术栈不限(可混合 React、Vue、Angular 等)。

示例技术栈(可替换):
  • 主应用:Vue 3 + Vite
  • 微应用 1:Vue 2 + Webpack
  • 微应用 2:React + Create React App

二、实现步骤

1. 主应用(容器应用)配置
(1)创建并初始化主应用
# 创建 Vue 3 主应用(以 Vite 为例)
npm create vite@latest main-app -- --template vue
cd main-app
npm install
(2)安装 qiankun
npm install qiankun
(3)注册微应用并启动 qiankun

在主应用入口文件(如 src/main.js)中配置:

import { createApp } from 'vue'
import App from './App.vue'
import { registerMicroApps, start } from 'qiankun'

// 1. 注册微应用
registerMicroApps([
  {
    name: 'vue2-app', // 微应用名称(唯一)
    entry: '//localhost:8081', // 微应用入口(开发环境地址)
    container: '#micro-container', // 微应用挂载的 DOM 容器
    activeRule: '/vue2', // 激活路由(当 URL 包含此路径时加载该微应用)
    props: { token: 'main-app-token' } // 传递给微应用的参数
  },
  {
    name: 'react-app',
    entry: '//localhost:3000',
    container: '#micro-container',
    activeRule: '/react',
    props: { userInfo: { name: 'main' } }
  }
])

// 2. 启动 qiankun(可选配置:沙箱、预加载等)
start({
  sandbox: {
    strictStyleIsolation: true // 严格样式隔离(推荐)
  },
  prefetch: 'all' // 预加载所有微应用资源(优化体验)
})

createApp(App).mount('#app')
(4)主应用页面布局(添加微应用容器和路由)

修改 src/App.vue,添加微应用挂载容器和导航:

<template>
  <div>
    <!-- 主应用导航 -->
    <nav>
      <a href="/vue2">Vue2 微应用</a>
      <a href="/react">React 微应用</a>
    </nav>
    
    <!-- 微应用挂载点 -->
    <div id="micro-container"></div>
  </div>
</template>

<style>
nav {
  padding: 20px;
  background: #f5f5f5;
}
a {
  margin: 0 10px;
  text-decoration: none;
  color: #333;
}
a:hover {
  color: #1890ff;
}
</style>
(5)配置主应用路由(可选,用于主应用自身页面)

如果主应用需要自己的页面(如首页),可添加路由(以 Vue Router 为例):

npm install vue-router

创建 src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', component: () => import('../views/Home.vue') }
]

export default createRouter({
  history: createWebHistory(),
  routes
})

main.js 中引入路由:

import router from './router'
createApp(App).use(router).mount('#app')
2. 微应用配置(以 Vue 2 为例)
(1)创建并初始化 Vue 2 微应用
# 使用 Vue CLI 创建 Vue 2 应用
vue create vue2-app
cd vue2-app
npm install
(2)修改微应用入口文件(src/main.js

使微应用支持被 qiankun 加载:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

let instance = null

// 渲染函数(支持独立运行和微应用模式)
function render(props = {}) {
  const { container } = props
  // 挂载到主应用的容器中(或自己的 #app)
  instance = new Vue({
    router,
    render: h => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app')
}

// 独立运行时(不被 qiankun 加载时)
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

// 微应用模式:暴露生命周期钩子给 qiankun
export async function bootstrap() {
  console.log('Vue2 微应用 bootstrap')
}

export async function mount(props) {
  console.log('Vue2 微应用 mount,接收参数:', props)
  render(props) // 传入主应用的参数
}

export async function unmount() {
  console.log('Vue2 微应用 unmount')
  instance.$destroy() // 销毁实例
  instance = null
}
(3)配置微应用打包(vue.config.js

允许跨域访问并指定微应用资源路径:

module.exports = {
  devServer: {
    port: 8081, // 微应用端口(与主应用注册的 entry 一致)
    headers: {
      'Access-Control-Allow-Origin': '*' // 允许跨域(关键)
    }
  },
  configureWebpack: {
    output: {
      library: 'vue2-app', // 微应用名称(与主应用注册的 name 一致)
      libraryTarget: 'umd', // 打包格式(支持 AMD/CommonJS)
      jsonpFunction: `webpackJsonp_vue2_app` // 避免全局变量冲突
    }
  }
}
(4)微应用路由配置(src/router/index.js

路由基础路径需与主应用的 activeRule 一致:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

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

const router = new VueRouter({
  mode: 'history',
  // 基础路径:主应用激活路由 + 微应用路由(确保不冲突)
  base: window.__POWERED_BY_QIANKUN__ ? '/vue2' : '/',
  routes
})

export default router
3. 微应用配置(以 React 为例)
(1)创建并初始化 React 微应用
npx create-react-app react-app
cd react-app
npm install
(2)安装 react-app-rewired 自定义配置
npm install react-app-rewired --save-dev
(3)修改 package.json 启动脚本
{
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test"
  }
}
(4)配置微应用打包(config-overrides.js
module.exports = {
  webpack: (config) => {
    config.output.library = 'react-app';
    config.output.libraryTarget = 'umd';
    config.output.publicPath = '//localhost:3000/'; // 微应用资源路径
    return config;
  },
  devServer: (configFunction) => {
    return (proxy, allowedHost) => {
      const config = configFunction(proxy, allowedHost);
      config.port = 3000; // 微应用端口
      config.headers = {
        'Access-Control-Allow-Origin': '*' // 允许跨域
      };
      return config;
    };
  }
};
(5)修改入口文件(src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

let root = null;

// 渲染函数
function render(props = {}) {
  const { container } = props;
  const dom = container ? container.querySelector('#root') : document.getElementById('root');
  root = ReactDOM.createRoot(dom);
  root.render(
    <BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/react' : '/'}>
      <App />
    </BrowserRouter>
  );
}

// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// 暴露生命周期钩子
export async function bootstrap() {
  console.log('React 微应用 bootstrap');
}

export async function mount(props) {
  console.log('React 微应用 mount,接收参数:', props);
  render(props);
}

export async function unmount() {
  console.log('React 微应用 unmount');
  root.unmount();
}

三、启动应用

  1. 启动主应用:

    cd main-app
    npm run dev # 默认端口 5173
    
  2. 启动 Vue 2 微应用:

    cd vue2-app
    npm run serve # 端口 8081
    
  3. 启动 React 微应用:

    cd react-app
    npm start # 端口 3000
    
  4. 访问主应用:http://localhost:5173,点击导航可切换微应用。

四、核心功能扩展

1. 主应用与微应用通信
(1)主应用向微应用传递参数

通过 registerMicroApps 中的 props 字段(已在步骤 2.1 中演示)。

(2)微应用向主应用发送消息

微应用中可通过 props.onGlobalStateChangeprops.setGlobalState 通信(需主应用初始化全局状态):

主应用初始化全局状态(main.js):

import { initGlobalState } from 'qiankun'

// 初始化全局状态
const actions = initGlobalState({
  user: null,
  theme: 'light'
})

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

// 注册微应用时传递 actions
registerMicroApps([
  {
    // ...其他配置
    props: { actions } // 传递全局状态控制器
  }
])

微应用中使用(以 Vue 2 为例):

// 在 mount 钩子中接收 actions
export async function mount(props) {
  // 监听主应用状态
  props.actions.onGlobalStateChange((state) => {
    console.log('微应用接收主应用状态:', state)
  })
  
  // 向主应用发送状态
  props.actions.setGlobalState({ user: 'micro-app-user' })
}
2. 样式隔离

qiankun 提供两种样式隔离方案,在 start 中配置:

start({
  sandbox: {
    strictStyleIsolation: true, // 严格隔离(推荐,使用 Shadow DOM)
    // 或
    experimentalStyleIsolation: true // 实验性隔离(通过 CSS 前缀)
  }
})
3. 公共依赖共享

避免重复加载 React、Vue 等框架,主应用中配置:

start({
  // 共享依赖(微应用无需再次加载)
  importEntryOpts: {
    externals: ['vue', 'react']
  }
})

五、部署注意事项

  1. 生产环境中,微应用的 entry 需改为实际部署的 URL(如 https://micro-app1.example.com)。
  2. 确保主应用和微应用的路由不冲突(通过 activeRule 和微应用路由 base 区分)。
  3. 微应用打包后需部署到支持跨域的服务器(生产环境可通过 Nginx 配置 Access-Control-Allow-Origin)。
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐