微前端(qiankun)使用教程
微前端(qiankun)使用教程
一、什么是微前端
微前端(Micro-Frontends)是一种架构模式,它将前端应用分解为一系列可独立开发、测试、部署的小型应用(微应用),并能将这些微应用组合成一个完整的应用。其核心思想借鉴了后端微服务架构,旨在解决大型前端应用在长期维护中出现的代码臃肿、团队协作低效、技术栈锁定等问题。
- 技术栈无关:各微应用可采用不同技术栈(如 React、Vue、Angular 等)
- 独立开发部署:各团队可独立开发、测试、发布自己的微应用,不影响整体
- 增量升级:无需一次性重构整个应用,可逐步替换旧系统
- 团队自治:不同团队负责不同微应用,降低协作成本
- 复用与共享:公共组件和逻辑可抽离为共享依赖
二、微前端和iframe对比
1. 微前端(Micro-Frontends)
- 本质:将一个大型前端应用拆分为多个独立开发、部署的 “微应用”,通过容器应用(Host)实现微应用的加载、卸载、路由分发和通信,最终组合成一个完整应用。
- 技术核心:基于路由匹配或手动触发加载微应用资源(JS/CSS/HTML),通过沙箱隔离(JS/CSS)、全局状态管理或事件总线实现通信,微应用与容器共享同一个浏览器上下文(window、DOM 根节点等)。
2. iframe
- 本质:通过 HTML 原生标签
<iframe>
嵌入另一个完整的 HTML 文档,形成一个 “页面中的页面”。 - 技术核心:每个
iframe
拥有独立的浏览器上下文(独立的window
、document
、JS 执行环境和 DOM 树),与父页面通过postMessage
或window.parent
进行有限通信。
3. 关键区别对比
对比维度 | 微前端 | iframe |
---|---|---|
上下文隔离 | 共享同一个 window 上下文,通过沙箱(如 Proxy)隔离全局变量 | 完全隔离,每个 iframe 有独立 window 和 DOM 树 |
样式隔离 | 需要手动实现(如 CSS Modules、Shadow DOM、BEM 命名) | 天然隔离(iframe 内部样式不影响父页面,反之亦然) |
通信方式 | 灵活多样:全局事件总线、共享状态库(Redux/Vuex)、Props 传递 | 仅支持 postMessage 或 window.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();
}
三、启动应用
-
启动主应用:
cd main-app npm run dev # 默认端口 5173
-
启动 Vue 2 微应用:
cd vue2-app npm run serve # 端口 8081
-
启动 React 微应用:
cd react-app npm start # 端口 3000
-
访问主应用:
http://localhost:5173
,点击导航可切换微应用。
四、核心功能扩展
1. 主应用与微应用通信
(1)主应用向微应用传递参数
通过 registerMicroApps
中的 props
字段(已在步骤 2.1 中演示)。
(2)微应用向主应用发送消息
微应用中可通过 props.onGlobalStateChange
和 props.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']}
})
五、部署注意事项
- 生产环境中,微应用的
entry
需改为实际部署的 URL(如https://micro-app1.example.com
)。 - 确保主应用和微应用的路由不冲突(通过
activeRule
和微应用路由base
区分)。 - 微应用打包后需部署到支持跨域的服务器(生产环境可通过 Nginx 配置
Access-Control-Allow-Origin
)。