隨着項目越做越大,團隊協作越來越困難——改一處代碼影響全局,每個團隊都得用同樣的技術棧,發佈還得整體部署。這些問題在做企業級中台系統時特別明顯,後來接觸到微應用架構才找到解決方案:把一個大應用拆成多個獨立的小應用,各自開發、部署,最後再整合到一起。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/';
}

六、避坑指南

  1. 樣式隔離問題:主應用和子應用的樣式可能衝突,開啓 strictStyleIsolation: true 後,子應用的樣式會被自動添加前綴。如果需要共享樣式,可放在主應用或通過 global 關鍵字聲明全局樣式。
  2. 路由模式衝突:主應用和子應用盡量都用 history 模式,子應用的 base 路由必須和主應用的 activeRule 一致,否則會出現路由錯亂。
  3. 依賴共享:如果多個子應用都用到 Vue、React 等庫,可以在主應用中加載,子應用不打包這些依賴,減少重複加載:
// 主應用引入共享依賴
import Vue from 'vue';
window.Vue = Vue;

// 子應用 webpack 配置排除依賴
configureWebpack: {
  externals: ['vue', 'vue-router']
}
  1. 子應用卸載清理:子應用 unmount 時要銷燬實例、清除定時器和事件監聽,否則可能導致內存泄漏:
export async function unmount() {
  instance.$destroy();
  clearInterval(timer); // 清除定時器
  window.removeEventListener('resize', handleResize); // 清除事件監聽
}
  1. 跨域問題:生產環境中子應用必須允許主應用域名跨域,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 讓微應用落地變得簡單,核心步驟就是:主應用註冊子應用 -> 子應用暴露生命週期 -> 處理通信和樣式隔離。它的優勢在於對現有應用入侵性小,不需要完全重構就能接入微應用架構。

但微應用不是銀彈,適合團隊多、應用大的場景。如果項目規模小,強行拆分反而會增加複雜度。實際使用時,要根據團隊規模和業務複雜度決定是否採用,重點關注應用拆分粒度(按業務域劃分)和通信機制設計,讓每個子應用真正做到獨立開發、獨立部署。