微前端从入门到实践教程

微前端从入门到实践教程

欢迎来到这份微前端教程!随着前端应用变得越来越复杂,传统的单体应用架构逐渐暴露出其局限性。微前端作为一种借鉴了微服务思想的架构模式,正成为解决这些挑战的强大工具。本教程将带您深入理解微前端的核心概念、技术原理,并通过实践案例帮助您掌握构建和管理微前端应用的能力。

第一部分:微前端基础认知

1. 引言:前端开发的演进与挑战

在过去的十年中,前端开发经历了飞速的发展。从最初的静态网页到JQuery时代,再到MV*框架(如Angular, React, Vue)的兴起,前端应用变得越来越庞大和复杂。随之而来的,是“巨石应用”(Monolithic Application)架构的痛点:

后端微服务架构的成功启发了前端开发者:如果后端可以将一个大型服务拆分成多个独立、可部署的小服务,为什么前端不能也这样做呢?这就是微前端思想的起源。

2. 什么是微前端?

微前端(Micro-Frontends)是一种架构风格,它将一个大型前端应用拆分成多个独立可部署的小型应用,这些小型应用可以由不同的团队、使用不同的技术栈独立开发、部署和维护,最终聚合在一个主应用(基座)中,呈现给用户一个统一的、无缝的用户体验。

核心理念

微前端的优势

微前端的挑战

当然,微前端并非银弹,它也带来了一些新的挑战:

3. 微前端的常见架构模式

为了解决上述挑战,社区涌现了多种微前端的实现架构模式:

路由分发式

这是最简单也最常见的模式。通常基于后端路由(如Nginx、CDN)或者前端路由重定向,将不同路径的请求转发到不同的独立部署的应用。

graph TD A[用户浏览器] -->|请求 /app1/home| B(Nginx/CDN) A -->|请求 /app2/dashboard| B B -->|转发到 app1 服务| C(独立部署的 App1) B -->|转发到 app2 服务| D(独立部署的 App2)

优点: 部署完全独立,技术栈完全自由,隔离性好。
缺点: 用户体验可能存在切换页面时的刷新,公共头部/导航等难以共享,不适合需要紧密协作的子应用。

iframe模式

使用iframe标签将子应用嵌入到主应用中。每个iframe提供了一个独立的运行环境,包括DOM、JS和CSS的完全隔离。

<!-- 主应用 index.html -->
<div id="main-container"></div>
<iframe src="/path-to-subapp1/" id="subapp1-iframe"></iframe>
<iframe src="/path-to-subapp2/" id="subapp2-iframe" style="display: none;"></iframe>

优点: 隔离性最佳,最简单易行。
缺点: 性能开销大(每个iframe都有独立的JS/CSS/DOM上下文),路由同步困难,无法直接共享上下文,通信复杂。

Web Components模式

利用Web Components(Custom Elements, Shadow DOM, HTML Templates, ES Modules)来构建可复用的独立组件,并将这些组件作为微前端的单元集成。每个子应用都可以是一个或多个Web Components。

<!-- 主应用 index.html -->
<!-- 定义 custom element <my-app-header> 和 <my-app-dashboard> -->
<my-app-header></my-app-header>
<my-app-dashboard></my-app-dashboard>

优点: 原生支持,组件级别集成,隔离性较好(Shadow DOM),可复用性高。
缺点: 浏览器兼容性(需要Polyfill),学习曲线,组件间通信和状态管理仍需额外处理。

JS沙箱模式(如qiankun)

这是目前最主流的微前端框架(如qiankun、single-spa)采用的模式。通过JavaScript动态加载和执行子应用的代码,并利用JS沙箱(with 语句、Proxy 对象)和CSS沙箱(样式隔离技术)来模拟独立环境,同时又能在主应用中共享一些资源。

优点: 用户体验流畅(无刷新切换),隔离性与共享性兼顾,主流框架支持成熟。
缺点: 实现相对复杂,需要对子应用进行改造以适配框架。

第二部分:微前端核心技术与实现

1. 应用注册与加载

在JS沙箱模式中,主应用(基座)需要知道有哪些子应用,以及如何加载和卸载它们。

加载原理: 基座通过路由判断当前应该渲染哪个子应用,然后动态地向浏览器注入该子应用的HTML、CSS和JS资源,并执行其入口代码。在子应用切换时,会调用前一个子应用的卸载钩子,再加载并挂载新的子应用。

2. 路由管理

微前端中的路由管理是一个核心挑战,目标是确保主应用和子应用之间的路由协调一致,提供无缝的用户体验。

3. JS 沙箱

JS沙箱的目的是隔离不同子应用之间的JavaScript运行环境,防止全局变量污染和冲突。

以Proxy沙箱为例,当子应用访问 window.foo 时,实际上是访问代理对象的 foo 属性。当子应用修改 window.bar = 'new' 时,也是在代理对象上进行操作,不会影响真实的 window

4. CSS 沙箱

CSS沙箱的目的是隔离不同子应用之间的样式,防止样式污染和冲突。

5. 应用间通信

虽然子应用之间应该尽可能独立,但在某些场景下,它们仍然需要进行通信,例如:用户登录状态同步、数据共享、事件通知等。

6. 公共依赖管理

多个子应用可能都会使用到相同的第三方库(如React、Vue、Lodash、Ant Design等)。如果每个子应用都单独打包这些库,会导致整体包体积过大,加载速度慢。

第三部分:主流微前端框架与实践

实现微前端涉及许多底层细节,为了简化开发,通常会使用成熟的微前端框架。

1. qiankun 框架

qiankun 是由蚂蚁金服开源的一套完整的微前端解决方案,基于 single-spa 进行二次开发,具有更强的开箱即用能力,并内置了JS沙箱、CSS沙箱、预加载等功能。

qiankun 介绍与核心功能

qiankun 的快速上手与示例

Step 1: 主应用 (基座) 配置

安装 qiankun:

npm install qiankun
# 或
yarn add qiankun

在主应用入口文件 (e.g., src/index.js) 中注册子应用并启动:

// src/index.js (主应用)
import { registerMicroApps, start } from 'qiankun';

registerMicroApps([
    {
        name: 'reactApp', // 唯一ID
        entry: '//localhost:8001', // 子应用地址
        container: '#container', // 子应用挂载的DOM节点
        activeRule: '/react', // 激活规则,当路径以 /react 开头时激活
        props: {
            msg: '来自主应用的消息',
        }, // 传递给子应用的props
    },
    {
        name: 'vueApp',
        entry: '//localhost:8002',
        container: '#container',
        activeRule: '/vue',
    },
]);

// 启动 qiankun
start({
    prefetch: true, // 开启预加载
    sandbox: {
        strictStyleIsolation: true, // 开启严格的CSS沙箱
        experimentalStyleIsolation: true, // 实验性CSS沙箱,防止全局样式污染
    },
});

// 路由监听
// 在主应用路由变化时(例如使用 React Router 或 Vue Router),
// 切换到对应的微前端路由前缀,qiankun会根据 activeRule 自动加载子应用。
// 示例:
// import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom';
// function App() {
//   return (
//     <Router>
//       <nav>
//         <Link to="/react">React App</Link>
//         <Link to="/vue">Vue App</Link>
//       </nav>
//       <div id="container"></div> <!-- 子应用挂载点 -->
//       <Switch>
//         <Route path="/react" render={() => null} />
//         <Route path="/vue" render={() => null} />
//       </Switch>
//     </Router>
//   );
// }
// ReactDOM.render(<App />, document.getElementById('root'));

Step 2: 子应用 (React/Vue) 配置

子应用需要导出特定的生命周期钩子。为了让 qiankun 识别并加载,需要改造子应用的入口文件。通常是修改Webpack配置,将子应用打包成UMD格式并暴露生命周期函数。

React 子应用 (src/index.js)

// src/index.js (React 子应用)
import React from 'react';
import ReactDOM from 'react-dom/client'; // React 18
import App from './App';

let root = null; // 用于存储 React 18 的 root 实例

function render(props) {
    const { container } = props;
    const domElement = container ? container.querySelector('#root') : document.getElementById('root');
    if (!root) {
        root = ReactDOM.createRoot(domElement);
    }
    root.render(<App />);
}

// 生命周期 - bootstrap:初始化
export async function bootstrap() {
    console.log('React micro app bootstrapped');
}

// 生命周期 - mount:挂载
export async function mount(props) {
    console.log('React micro app mounted', props);
    render(props);
}

// 生命周期 - unmount:卸载
export async function unmount(props) {
    console.log('React micro app unmounted', props);
    // React 18 卸载方式
    if (root) {
        root.unmount();
        root = null;
    }
    // React 17 及以下 ReactDOM.unmountComponentAtNode(container ? container.querySelector('#root') : document.getElementById('root'));
}

// 独立运行模式 (非qiankun环境)
if (!window.__POWERED_BY_QIANKUN__) {
    // 假设非qiankun环境下,<div id="root"></div> 就在当前文档中
    const initialRoot = ReactDOM.createRoot(document.getElementById('root'));
    initialRoot.render(<App />);
}

Vue 3 子应用 (src/main.js)

// src/main.js (Vue 子应用)
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 假设有 Vue Router

let instance = null;

function render(props = {}) {
    const { container } = props;
    instance = createApp(App);
    instance.use(router);
    instance.mount(container ? container.querySelector('#app') : '#app');
}

// 生命周期 - bootstrap:初始化
export async function bootstrap() {
    console.log('Vue micro app bootstrapped');
}

// 生命周期 - mount:挂载
export async function mount(props) {
    console.log('Vue micro app mounted', props);
    render(props);
}

// 生命周期 - unmount:卸载
export async function unmount() {
    console.log('Vue micro app unmounted');
    instance.unmount();
    instance = null;
}

// 独立运行模式 (非qiankun环境)
if (!window.__POWERED_BY_QIANKUN__) {
    render();
}

子应用 Webpack 配置 (webpack.config.js)

子应用需要配置Webpack,确保其能够正确地被 qiankun 加载,主要是配置 output.libraryoutput.libraryTarget

// 子应用 webpack.config.js
const { name } = require('./package.json'); // 获取 package.json 中的 name 字段

module.exports = {
    // ... 其他配置
    output: {
        library: `${name}-[name]`, // 库名称,推荐使用package.json中的name作为前缀
        libraryTarget: 'umd', // 打包成umd格式
        jsonpFunction: `webpackJsonp_${name}`, // 确保不同子应用之间jsonpFunction不冲突
        globalObject: 'window', // 确保在各种环境下都能正确获取全局对象
    },
    // ...
    devServer: {
        port: 8001, // 子应用端口
        headers: {
            'Access-Control-Allow-Origin': '*', // 允许跨域
        },
        // ...
    },
};

通过这些配置,qiankun 可以很好地管理子应用的加载、卸载和隔离。

2. Webpack Module Federation (模块联邦)

模块联邦是Webpack 5引入的杀手级特性,它从构建层面解决了多应用之间代码共享和动态加载的问题。它不仅仅是微前端的解决方案,更是一种通用的分布式应用架构模式。

模块联邦的核心概念与优势

优势:

如何使用模块联邦实现微前端

如前文“公共依赖管理”部分所示,通过配置 ModuleFederationPlugin,主应用可以作为Host消费子应用暴露的组件或整个子应用入口。子应用则作为Remote暴露自己的内容。同时,shared 配置可以确保公共依赖只加载一次。

与 qiankun 的对比与选择

特性 qiankun Webpack Module Federation
层级 应用层(运行时加载、隔离) 模块/组件层(编译时/运行时加载、共享)
技术栈 完全无关 构建于Webpack生态,对框架兼容性好但要求都使用Webpack
隔离机制 JS/CSS沙箱 依赖Webpack构建和Loader处理
依赖共享 运行时动态加载检查,或通过Externals 原生支持,基于shared配置
应用间通信 手动实现(Props、EventBus) 直接导入对方暴露的模块(更像组件调用)
适用场景 大型、复杂、多团队、多技术栈的应用,追求独立部署与隔离 构建大型单页应用内的微组件、微服务,或者对包体积和运行时性能有极致要求的多应用共享场景
上手难度 相对较低,配置较少 配置复杂,概念较多,需要深入理解Webpack

选择建议:

3. 其他框架简介

第四部分:微前端高级主题与最佳实践

1. 性能优化

微前端引入了额外的复杂性,可能导致性能问题。优化策略包括:

2. 部署与运维

3. 工程化与代码规范

4. 常见问题与解决方案

第五部分:实战案例:构建一个简易微前端应用 (基于 qiankun)

本节将通过一个简单的项目,演示如何使用 qiankun 框架搭建一个微前端应用。我们将构建一个基座应用和两个子应用(一个React子应用,一个Vue子应用)。

1. 项目搭建与规划

创建一个主应用目录 micro-frontend-demo,并在其中创建子应用目录:

mkdir micro-frontend-demo
cd micro-frontend-demo

# 创建主应用 (比如 Vite 或 CRA)
npx create-react-app main-app --template typescript # 或者 vite create main-app --template react-ts
cd main-app && npm install && cd ..

# 创建 React 子应用
npx create-react-app react-sub-app --template typescript
cd react-sub-app && npm install && cd ..

# 创建 Vue 子应用
npm init vue@latest # 或者 @vue/cli create vue-sub-app
cd vue-sub-app && npm install && cd ..

目录结构大致如下:

micro-frontend-demo/
├── main-app/             # 基座应用 (React/Vite)
├── react-sub-app/        # React 子应用
└── vue-sub-app/          # Vue 子应用

2. 基座应用开发

进入 main-app 目录,安装 qiankun

cd main-app
npm install qiankun react-router-dom # 如果是 React
# 或
npm install qiankun vue-router # 如果是 Vue

修改 main-app/src/index.tsx (或 .jsx / .js) 和 main-app/src/App.tsx

// main-app/src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
// main-app/src/App.tsx
import React, { useEffect } from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import { registerMicroApps, start } from 'qiankun';
import './App.css';

// 假设我们使用 React Router 的Link进行导航
// Link to="/react" 会触发 activeRule /react 从而加载 React 子应用
// Link to="/vue" 会触发 activeRule /vue 从而加载 Vue 子应用

function App() {
  useEffect(() => {
    registerMicroApps([
      {
        name: 'reactApp',
        entry: '//localhost:3001', // React 子应用的开发服务器地址
        container: '#sub-app-container',
        activeRule: '/react',
        props: {
          token: 'your_jwt_token_from_main_app',
          userInfo: { id: 1, name: 'Main App User' },
          onGlobalEvent: (data: string) => {
            console.log('Main App received event from sub-app:', data);
          },
        },
      },
      {
        name: 'vueApp',
        entry: '//localhost:3002', // Vue 子应用的开发服务器地址
        container: '#sub-app-container',
        activeRule: '/vue',
      },
    ]);

    start({
        prefetch: true, // 开启预加载
        sandbox: {
            strictStyleIsolation: true, // 严格的CSS沙箱,防止子应用样式污染主应用
            experimentalStyleIsolation: true, // 实验性的CSS沙箱,为每个子应用创建一个独立的 shadow DOM 容器
        },
    });
  }, []);

  return (
    <Router>
      <div className="main-app">
        <header className="main-header">
          <h1>主应用 (Base App)</h1>
          <nav>
            <Link to="/react" className="nav-link">React 子应用</Link>
            <Link to="/vue" className="nav-link">Vue 子应用</Link>
            <Link to="/" className="nav-link">主应用首页</Link>
          </nav>
        </header>
        <main className="main-content">
          <div id="sub-app-container">
            {/* 子应用会挂载到这里 */}
            {/* 如果当前路由没有匹配到子应用,可以显示主应用内容 */}
            {window.location.pathname === '/' && (
              <div>欢迎来到微前端首页!请点击上方导航切换子应用。</div>
            )}
          </div>
        </main>
      </div>
    </Router>
  );
}

export default App;

并为 main-app/src/App.css 添加一些样式:

/* main-app/src/App.css */
.main-app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.main-header {
    background-color: #2c3e50;
    color: white;
    padding: 15px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.main-header h1 {
    margin: 0;
    font-size: 1.8em;
}

.main-header nav .nav-link {
    color: white;
    margin-left: 20px;
    padding: 8px 15px;
    border-radius: 5px;
    transition: background-color 0.3s ease;
}

.main-header nav .nav-link:hover {
    background-color: #34495e;
    text-decoration: none;
}

.main-content {
    flex-grow: 1;
    padding: 20px;
    background-color: #f0f2f5;
    display: flex;
    justify-content: center;
    align-items: flex-start; /* 保持内容靠上 */
}

#sub-app-container {
    width: 100%; /* 子应用容器占据整个宽度 */
    max-width: 960px; /* 限制子应用最大宽度 */
    min-height: 500px; /* 保证容器有高度 */
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    padding: 20px;
}

启动主应用:

cd main-app
npm start # 或 yarn start 或 npm run dev

默认在 http://localhost:3000 运行。

3. 子应用开发(React/Vue)

React 子应用 (react-sub-app)

进入 react-sub-app 目录,安装 @craco/cracowebpack-bundle-analyzer (可选,用于分析打包文件)。 CRA 需要进行一些改造来暴露生命周期函数并支持跨域。

cd react-sub-app
npm install @craco/craco # CRA 项目改造工具

修改 package.json 的 scripts:

"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
}

在项目根目录创建 craco.config.js

// react-sub-app/craco.config.js
const { name } = require('./package.json');

module.exports = {
    webpack: {
        configure: (webpackConfig) => {
            webpackConfig.output = {
                ...webpackConfig.output,
                library: `${name}-[name]`,
                libraryTarget: 'umd',
                jsonpFunction: `webpackJsonp_${name}`,
                globalObject: 'window',
            };
            return webpackConfig;
        },
    },
    devServer: {
        port: 3001, // 确保端口与主应用配置一致
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },
};

修改 react-sub-app/src/index.tsx 文件以暴露 qiankun 生命周期:

// react-sub-app/src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';

let root: ReactDOM.Root | null = null;
let currentAppProps: any = {}; // 存储当前 props

function render(props: any) {
    const { container, onGlobalEvent } = props;
    currentAppProps = props; // 更新 props

    const domElement = container ? container.querySelector('#root') : document.getElementById('root');
    if (!root) {
        root = ReactDOM.createRoot(domElement as HTMLElement);
    }
    root.render(<React.StrictMode><App onGlobalEvent={onGlobalEvent} /></React.StrictMode>);
}

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

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

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

if (!window.__POWERED_BY_QIANKUN__) {
    // 独立运行模式
    root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
    root.render(<React.StrictMode><App onGlobalEvent={() => console.log('Standalone event')} /></React.StrictMode>);
}

修改 react-sub-app/src/App.tsx 接收主应用传来的 props 并发送事件:

// react-sub-app/src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';

interface AppProps {
  token?: string;
  userInfo?: { id: number; name: string; };
  onGlobalEvent?: (data: string) => void;
}

function App({ token, userInfo, onGlobalEvent }: AppProps) {
  const handleClick = () => {
    onGlobalEvent && onGlobalEvent('Hello from React sub-app!');
  };

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          这是 <code>React 子应用</code>!
        </p>
        <p>主应用传入的 Token: <code>{token || 'N/A'}</code></p>
        <p>主应用传入的用户信息: <code>{userInfo ? userInfo.name : 'N/A'}</code></p>
        <button onClick={handleClick} style={{ padding: '10px 20px', fontSize: '1em', cursor: 'pointer' }}>
          点击我向主应用发送事件
        </button>
      </header>
    </div>
  );
}

export default App;

启动 React 子应用:

cd react-sub-app
npm start # 默认在 http://localhost:3001 运行

Vue 子应用 (vue-sub-app)

进入 vue-sub-app 目录,修改 vue.config.js(如果不存在则创建):

// vue-sub-app/vue.config.js
const { name } = require('./package.json');

module.exports = {
    devServer: {
        port: 3002, // 确保端口与主应用配置一致
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },
    configureWebpack: {
        output: {
            library: `${name}-[name]`,
            libraryTarget: 'umd',
            jsonpFunction: `webpackJsonp_${name}`,
            globalObject: 'window',
        },
    },
};

修改 vue-sub-app/src/main.js 以暴露 qiankun 生命周期:

// vue-sub-app/src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 假设你创建了 Vue Router

let instance = null;

function render(props = {}) {
    const { container } = props;
    instance = createApp(App);
    instance.use(router); // 如果有 router
    instance.mount(container ? container.querySelector('#app') : '#app');
}

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

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

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

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

修改 vue-sub-app/src/App.vue

<template>
  <div id="app" style="text-align: center; padding: 20px; border: 1px dashed #42b983; border-radius: 8px; background-color: #e6ffe6;">
    <img alt="Vue logo" src="./assets/logo.svg" width="100" height="100">
    <h1>这是 Vue 子应用</h1>
    <p>Vue 版本: <code>{{ vueVersion }}</code></p>
    <router-link to="/about" style="margin-right: 15px;">关于页面</router-link>
    <router-link to="/">首页</router-link>
    <router-view />
  </div>
</template>

<script>
import { version } from 'vue';

export default {
  name: 'VueApp',
  setup() {
    const vueVersion = version;
    return {
      vueVersion,
    };
  }
}
</script>

<style>
/* Vue 子应用内部样式 */
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
</style>

为 Vue 子应用添加路由 (vue-sub-app/src/router/index.js):

// vue-sub-app/src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const routes = [
  {
    path: '/',
    name: 'home',
    component: { template: '<h2>Vue 子应用 Home</h2>' },
  },
  {
    path: '/about',
    name: 'about',
    component: { template: '<h2>Vue 子应用 About</h2><p>这是 Vue 的 about 页面。</p>' },
  },
];

// 注意:在微前端环境中,createWebHistory 可能需要一个 base
// qiankun 会自动劫持路由,所以这里可以保持默认或按需配置
const router = createRouter({
  history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/vue/' : '/'), // 在 qiankun 环境下为子应用添加 base
  routes,
});

export default router;

启动 Vue 子应用:

cd vue-sub-app
npm run serve # 默认在 http://localhost:3002 运行

4. 通信与集成演示

现在,您应该可以同时运行三个应用:

访问 http://localhost:3000,点击“React 子应用”或“Vue 子应用”链接。您将看到相应的子应用无刷新地加载到主应用的 #sub-app-container 中。

5. 部署与验证

在实际生产环境中,每个应用都需要独立部署。以 React 子应用为例:

cd react-sub-app
npm run build

这会在 react-sub-app/build 目录下生成生产环境的静态文件。将这些文件部署到您的静态文件服务器(如Nginx、OSS、CDN)上,并确保其可以通过某个URL访问(例如 https://your-cdn.com/react-sub-app/)。

然后,在主应用的 registerMicroApps 中将 entry 地址修改为生产环境的URL:

// main-app/src/App.tsx
{
    name: 'reactApp',
    entry: 'https://your-cdn.com/react-sub-app/', // 生产环境地址
    container: '#sub-app-container',
    activeRule: '/react',
}

重新构建并部署主应用。这样,当用户访问主应用时,它将从CDN加载子应用资源。

恭喜! 您已经完成了从微前端基础概念到实际项目搭建的全过程。微前端是一个强大的工具,能够帮助您构建更具可伸缩性、可维护性和协作性的前端应用。虽然它引入了一些新的复杂性,但通过理解其核心原理并选择合适的框架,您将能够有效地驾驭它。

祝您在微前端的实践中一切顺利!

互动区域

登录后可以点赞此内容

参与互动

登录后可以点赞和评论此内容,与作者互动交流