隨着項目越做越大,團隊協作越來越困難——改一處代碼影響全局,每個團隊都得用同樣的技術棧,發佈還得整體部署。這些問題在做企業級中台系統時特別明顯,後來接觸到微應用架構才找到解決方案:把一個大應用拆成多個獨立的小應用,各自開發、部署,最後再整合到一起。qiankun 就是目前最成熟的微應用框架,基於 single-spa 封裝,對 Vue、React 等框架兼容性很好。
一、微應用架構基礎:主應用與子應用
微應用架構的核心是“主應用 + 子應用”模式:
- 主應用:負責整合所有子應用,提供公共導航、權限管理等基礎能力。
- 子應用:獨立的前端應用(可以用不同技術棧),能被主應用加載和卸載。
舉個電商平台的例子,主應用負責頂部導航和用户信息,子應用可以是商品列表、購物車、訂單管理等,每個子應用由不同團隊開發。
二、主應用接入:搭建整合平台
主應用的作用是“容器”,需要引入 qiankun 並註冊子應用。以 Vue 主應用為例:
1. 安裝 qiankun
npm install qiankun --save
2. 註冊子應用
在主應用的入口文件(如 main.js)中配置:
import { registerMicroApps, start } from 'qiankun';
// 註冊子應用
registerMicroApps([
{
name: 'product-app', // 子應用名稱(唯一)
entry: '//localhost:8081', // 子應用入口地址(開發環境)
container: '#subapp-container', // 子應用掛載的 DOM 節點
activeRule: '/product', // 觸發加載子應用的路由
props: { token: 'xxx' } // 傳給子應用的參數
},
{
name: 'cart-app',
entry: '//localhost:8082',
container: '#subapp-container',
activeRule: '/cart'
}
]);
// 啓動 qiankun
start({
sandbox: { strictStyleIsolation: true } // 開啓樣式隔離
});
3. 主應用頁面佈局
在主應用的頁面中預留子應用掛載點:
<!-- App.vue -->
<template>
<div>
<!-- 主應用導航 -->
<nav>
<router-link to="/product">商品</router-link>
<router-link to="/cart">購物車</router-link>
</nav>
<!-- 子應用掛載容器 -->
<div id="subapp-container"></div>
</div>
</template>
三、子應用改造:讓應用可被嵌入
子應用需要做一些改造才能被主應用識別和加載,以 Vue 子應用為例:
1. 修改入口文件
子應用的 main.js 需要暴露生命週期鈎子:
import Vue from 'vue';
import App from './App.vue';
import router from './router';
let instance = null;
// 渲染函數
function render(props = {}) {
const { container } = props;
instance = new Vue({
router,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 獨立運行時直接渲染
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// 暴露給 qiankun 的生命週期
export async function bootstrap() {
console.log('product-app bootstrap');
}
export async function mount(props) {
console.log('product-app mount', props);
render(props); // 傳入主應用的參數
}
export async function unmount() {
console.log('product-app unmount');
instance.$destroy();
instance = null;
}
2. 配置跨域和公共路徑
子應用的 vue.config.js 需要允許主應用跨域訪問,並設置正確的公共路徑:
module.exports = {
// 允許主應用跨域
devServer: {
port: 8081,
headers: {
'Access-Control-Allow-Origin': '*'
}
},
// 配置 webpack
configureWebpack: {
output: {
library: `product-app`,
libraryTarget: 'umd', // 打包成 umd 格式
jsonpFunction: `webpackJsonp_product_app`
}
}
};
3. 路由配置
子應用的路由需要添加基礎路徑,避免與主應用路由衝突:
// 子應用 router/index.js
const router = new VueRouter({
mode: 'history',
// 獨立運行時用 '/',被嵌入時用主應用傳遞的 base
base: window.__POWERED_BY_QIANKUN__ ? '/product' : '/',
routes
});
四、通信機制:主應用與子應用數據交互
微應用間的通信主要有兩種方式:props 傳值和全局事件總線。
1. 主應用向子應用傳值
通過 registerMicroApps 時的 props 字段傳遞,子應用在 mount 鈎子中接收:
// 主應用註冊時傳值
registerMicroApps([{
// ...
props: { userInfo: { name: '張三' }, onLogin: () => {} }
}]);
// 子應用 mount 時接收
export async function mount(props) {
console.log('收到主應用數據:', props.userInfo);
// 調用主應用方法
props.onLogin();
render(props);
}
2. 子應用向主應用通信
用 qiankun 提供的 initGlobalState 創建全局狀態:
// 主應用創建全局狀態
import { initGlobalState } from 'qiankun';
const actions = initGlobalState({
token: '',
user: null
});
// 主應用監聽狀態變化
actions.onGlobalStateChange((state, prev) => {
console.log('狀態變化:', state, prev);
});
// 子應用獲取全局狀態並修改
export async function mount(props) {
// 獲取全局狀態
const { onGlobalStateChange, setGlobalState } = props;
// 監聽狀態變化
onGlobalStateChange((state) => {
console.log('子應用收到全局狀態:', state);
}, true);
// 修改全局狀態
setGlobalState({ token: 'new-token' });
}
五、部署實踐:獨立部署與集成
微應用的優勢之一是獨立部署,主應用和子應用可以分開發布。
1. 開發環境
開發時主應用和子應用分別啓動,子應用通過本地端口暴露服務(如 //localhost:8081),主應用配置對應的 entry 即可。
2. 生產環境
- 子應用部署到各自的服務器,比如:
- 商品子應用:
https://product.example.com - 購物車子應用:
https://cart.example.com
- 主應用部署到主域名
https://example.com,修改註冊配置:
registerMicroApps([
{
name: 'product-app',
entry: 'https://product.example.com', // 生產環境地址
activeRule: '/product'
}
]);
3. 靜態資源處理
子應用的靜態資源(圖片、CSS)如果用相對路徑,可能會加載失敗,建議用絕對路徑或配置 publicPath:
// 子應用 main.js
if (window.__POWERED_BY_QIANKUN__) {
// 被嵌入時,靜態資源路徑指向子應用自己的域名
__webpack_public_path__ = 'https://product.example.com/';
}
六、避坑指南
- 樣式隔離問題:主應用和子應用的樣式可能衝突,開啓
strictStyleIsolation: true後,子應用的樣式會被自動添加前綴。如果需要共享樣式,可放在主應用或通過global關鍵字聲明全局樣式。 - 路由模式衝突:主應用和子應用盡量都用
history模式,子應用的base路由必須和主應用的activeRule一致,否則會出現路由錯亂。 - 依賴共享:如果多個子應用都用到 Vue、React 等庫,可以在主應用中加載,子應用不打包這些依賴,減少重複加載:
// 主應用引入共享依賴
import Vue from 'vue';
window.Vue = Vue;
// 子應用 webpack 配置排除依賴
configureWebpack: {
externals: ['vue', 'vue-router']
}
- 子應用卸載清理:子應用
unmount時要銷燬實例、清除定時器和事件監聽,否則可能導致內存泄漏:
export async function unmount() {
instance.$destroy();
clearInterval(timer); // 清除定時器
window.removeEventListener('resize', handleResize); // 清除事件監聽
}
- 跨域問題:生產環境中子應用必須允許主應用域名跨域,Nginx 配置示例:
server {
listen 80;
server_name product.example.com;
location / {
add_header Access-Control-Allow-Origin https://example.com;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Allow-Headers *;
}
}
總結
qiankun 讓微應用落地變得簡單,核心步驟就是:主應用註冊子應用 -> 子應用暴露生命週期 -> 處理通信和樣式隔離。它的優勢在於對現有應用入侵性小,不需要完全重構就能接入微應用架構。
但微應用不是銀彈,適合團隊多、應用大的場景。如果項目規模小,強行拆分反而會增加複雜度。實際使用時,要根據團隊規模和業務複雜度決定是否採用,重點關注應用拆分粒度(按業務域劃分)和通信機制設計,讓每個子應用真正做到獨立開發、獨立部署。