跳转至

微前端架构

微前端架构

📚 章节目标

本章节将全面介绍微前端架构的各种技术和方案,包括qiankun、single-spa、Module Federation等,帮助学习者掌握微前端架构的核心方法。

学习目标

  1. 理解微前端架构的核心概念
  2. 掌握qiankun微前端框架
  3. 掌握single-spa微前端
  4. 掌握Module Federation
  5. 理解微前端通信机制
  6. 掌握微前端最佳实践

🏗️ 微前端架构概述

1. 什么是微前端

JavaScript
// 微前端定义
// 将前端应用拆分成多个小型、独立的应用
// 每个应用可以独立开发、部署、运行
// 最终组合成一个完整的应用

// 微前端的优势
// 1. 独立开发 - 团队可以独立开发各自的应用
// 2. 独立部署 - 每个应用可以独立部署
// 3. 技术栈无关 - 可以使用不同的技术栈
// 4. 增量升级 - 可以逐步升级技术栈
// 5. 故障隔离 - 一个应用故障不会影响其他应用

// 微前端的挑战
// 1. 应用间通信 - 需要设计通信机制
// 2. 共享依赖 - 需要处理共享依赖
// 3. 样式隔离 - 需要处理样式冲突
// 4. 性能优化 - 需要优化加载性能

2. 微前端架构模式

JavaScript
// 微前端架构模式
// 1. 基于路由 - 根据路由加载不同应用
// 2. 基于组件 - 将应用作为组件加载
// 3. 基于iframe - 使用iframe隔离应用
// 4. 基于Web Components - 使用Web Components技术

// 基于路由的微前端
const routes = {
  '/home': loadApp('home'),
  '/dashboard': loadApp('dashboard'),
  '/settings': loadApp('settings'),
};

// 基于组件的微前端
function App() {
  return (
    <div>
      <HomeApp />
      <DashboardApp />
      <SettingsApp />
    </div>
  );
}

🎭 qiankun微前端框架

1. qiankun基础

1.1 主应用配置

JavaScript
// 安装qiankun
npm install qiankun

// 主应用入口
import { registerMicroApps, start } from 'qiankun';

// 注册微应用
registerMicroApps([
  {
    name: 'react-app',
    entry: '//localhost:7100',
    container: '#subapp-viewport',
    activeRule: '/react',
  },
  {
    name: 'vue-app',
    entry: '//localhost:7200',
    container: '#subapp-viewport',
    activeRule: '/vue',
  },
  {
    name: 'angular-app',
    entry: '//localhost:7300',
    container: '#subapp-viewport',
    activeRule: '/angular',
  },
]);

// 启动qiankun
start({
  sandbox: {
    // 二选一:strictStyleIsolation使用Shadow DOM,兼容性较差
    // experimentalStyleIsolation使用样式前缀,兼容性更好
    experimentalStyleIsolation: true,
  },
});

1.2 子应用配置

JavaScript
// React子应用
// src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// src/index.js
import './public-path';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

let root = null;

function render(props = {}) {
  const { container } = props;
  const dom = container
    ? container.querySelector('#root')
    : document.querySelector('#root');

  root = ReactDOM.createRoot(dom);
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log('[react-app] bootstrap');
}

export async function mount(props) {
  console.log('[react-app] mount', props);
  render(props);
}

export async function unmount(props) {
  console.log('[react-app] unmount', props);
  root?.unmount();
  root = null;
}

// Vue 3 子应用
// 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);
  const dom = container ? container.querySelector('#app') : '#app';
  app.mount(dom);
}

if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log('[vue-app] bootstrap');
}

export async function mount(props) {
  console.log('[vue-app] mount', props);
  render(props);
}

export async function unmount(props) {
  console.log('[vue-app] unmount', props);
  app?.unmount();
  app = null;
}

2. qiankun高级特性

2.1 应用间通信

JavaScript
// 主应用
import { initGlobalState } from 'qiankun';

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

// 监听状态变化
actions.onGlobalStateChange((state, prev) => {
  console.log('State changed:', state, prev);
});

// 子应用(通过 mount 生命周期的 props 获取状态 API)
// React子应用
import { useState, useEffect, useCallback } from 'react';

let qiankunProps = null;

// 在 qiankun 生命周期中保存 props
export async function mount(props) {
  qiankunProps = props;
  render(props);
}

function App() {
  const [globalState, setLocalState] = useState({ user: null, token: '' });  // 解构赋值:从对象/数组提取值

  useEffect(() => {
    // 通过 qiankun props 监听全局状态变化
    qiankunProps?.onGlobalStateChange((state) => {
      setLocalState(state);
    }, true); // true = 立即触发一次回调
  }, []);

  const handleLogin = useCallback(() => {
    qiankunProps?.setGlobalState({
      user: { name: 'John' },
      token: 'abc123',
    });
  }, []);

  return (
    <div>
      <p>User: {globalState.user?.name}</p>
      <button onClick={handleLogin}>Login</button>
    </div>
  );
}

// Vue 3 子应用
// 通过 mount 生命周期保存 props,再在组件中使用
import { ref } from 'vue';

let qiankunActions = null;

export async function mount(props) {
  qiankunActions = props;
  render(props);
}

// 在组件中使用
const globalState = ref({});

// setup 中初始化
qiankunActions?.onGlobalStateChange((state) => {
  globalState.value = state;
}, true);

function updateGlobalState() {
  qiankunActions?.setGlobalState({
    user: { name: 'Jane' },
  });
}

2.2 样式隔离

JavaScript
// qiankun样式隔离(两种方式二选一)

// 方式一:experimentalStyleIsolation(推荐,兼容性好,添加样式前缀)
start({
  sandbox: {
    experimentalStyleIsolation: true,
  },
});

// 方式二:strictStyleIsolation(使用Shadow DOM,兼容性较差)
// start({
//   sandbox: {
//     strictStyleIsolation: true,
//   },
// });

// 子应用样式
/* 子应用样式会自动添加前缀 */
.container {
  background: #f0f0f0;
}

🎪 single-spa微前端

1. single-spa基础

1.1 主应用配置

JavaScript
// 安装single-spa
npm install single-spa

// 主应用入口
import { registerApplication, start } from 'single-spa';

// 注册微应用
registerApplication({
  name: 'react-app',
  app: () => System.import('react-app'),
  activeWhen: (location) => location.pathname.startsWith('/react'),
});

registerApplication({
  name: 'vue-app',
  app: () => System.import('vue-app'),
  activeWhen: (location) => location.pathname.startsWith('/vue'),
});

// 启动single-spa
start({
  urlRerouteOnly: true,
});

2.2 子应用配置

JavaScript
// React子应用
// src/root.component.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import Root from './root.component';

let root = null;

export const bootstrap = async () => {
  console.log('[react-app] bootstrap');
};

export const mount = async (props) => {
  console.log('[react-app] mount', props);
  const dom = document.getElementById('single-spa-application:react-app');
  root = ReactDOM.createRoot(dom);
  root.render(<Root />);
};

export const unmount = async (props) => {
  console.log('[react-app] unmount', props);
  root?.unmount();  // ?.可选链:对象为null/undefined时安全返回undefined
  root = null;
};

// Vue 3 子应用
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

let app = null;

export const bootstrap = async () => {
  console.log('[vue-app] bootstrap');
};

export const mount = async (props) => {
  console.log('[vue-app] mount', props);
  const dom = document.getElementById('single-spa-application:vue-app');
  app = createApp(App);
  app.use(router);
  app.mount(dom);
};

export const unmount = async (props) => {
  console.log('[vue-app] unmount', props);
  app?.unmount();
  app = null;
};

2. single-spa高级特性

2.1 应用间通信

JavaScript
// 使用single-spa的事件总线
// 主应用
import { navigateToUrl } from 'single-spa';

function handleNavigation(path) {
  navigateToUrl(path);
}

// 子应用
// 发送事件
window.dispatchEvent(new CustomEvent('custom-event', {
  detail: { data: 'Hello from React' },
}));

// 监听事件
window.addEventListener('custom-event', (event) => {
  console.log('Received event:', event.detail);
});

📦 Module Federation

1. Module Federation基础

1.1 主应用配置

JavaScript
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        reactApp: 'reactApp@http://localhost:7100/remoteEntry.js',
        vueApp: 'vueApp@http://localhost:7200/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, requiredVersion: deps.react },
        'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
        vue: { singleton: true, requiredVersion: deps.vue },
      },
    }),
  ],
};

// 使用远程模块
import React, { lazy, Suspense } from 'react';

const RemoteReactApp = lazy(() => import('reactApp/App'));
const RemoteVueApp = lazy(() => import('vueApp/App'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <RemoteReactApp />
        <RemoteVueApp />
      </Suspense>
    </div>
  );
}

1.2 子应用配置

JavaScript
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');  // const不可重新赋值;let块级作用域变量

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'reactApp',
      filename: 'remoteEntry.js',
      exposes: {
        './App': './src/App',
        './Button': './src/Button',
      },
      shared: {
        react: { singleton: true, requiredVersion: deps.react },
        'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
      },
    }),
  ],
};

2. Module Federation高级特性

2.1 动态加载远程模块

JavaScript
// 动态加载远程模块
async function loadRemoteModule() {  // async定义异步函数;await等待Promise完成
  const module = await import('reactApp/App');  // await等待异步操作完成
  return module.default;
}

// 使用
const RemoteApp = React.lazy(() => loadRemoteModule());  // 箭头函数:简洁的函数语法

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <RemoteApp />
    </Suspense>
  );
}

📝 练习题

1. 基础题

题目1:配置qiankun主应用

JavaScript
// 配置qiankun主应用
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
  // 注册微应用
]);

start();

2. 进阶题

题目2:实现微前端应用间通信

JavaScript
// 实现微前端应用间通信
// 主应用
import { initGlobalState } from 'qiankun';

// 子应用
// React子应用
// Vue子应用

3. 面试题

题目3:解释微前端的优势和挑战

JavaScript
// 答案要点:
// 优势:
// 1. 独立开发 - 团队可以独立开发
// 2. 独立部署 - 每个应用可以独立部署
// 3. 技术栈无关 - 可以使用不同的技术栈
// 4. 增量升级 - 可以逐步升级技术栈
// 5. 故障隔离 - 一个应用故障不会影响其他应用

// 挑战:
// 1. 应用间通信 - 需要设计通信机制
// 2. 共享依赖 - 需要处理共享依赖
// 3. 样式隔离 - 需要处理样式冲突
// 4. 性能优化 - 需要优化加载性能

🎯 本章总结

本章节全面介绍了微前端架构的各种技术和方案,包括qiankun、single-spa、Module Federation等。关键要点:

  1. 微前端概念:理解微前端架构的优势和挑战
  2. qiankun:掌握qiankun微前端框架的配置和使用
  3. single-spa:掌握single-spa微前端的配置和使用
  4. Module Federation:掌握Module Federation的配置和使用
  5. 应用间通信:掌握微前端应用间通信机制
  6. 最佳实践:理解微前端最佳实践

下一步将深入学习服务端渲染技术。