Webpack 5 Module Federation微前端实战:远程模块加载与共享依赖配置

微前端架构将大型单页应用拆分为独立部署的子应用,解决单体前端工程维护困难的问题。Webpack 5引入的Module Federation原生支持跨应用模块共享,无需额外框架即可实现远程模块动态加载。前端工程化实践中,Module Federation已成为构建可扩展微前端体系的关键技术方案。

Module Federation核心概念

Module Federation定义了两个角色:Host(消费者)和Remote(提供者)。Remote应用通过webpack配置暴露组件或模块,Host应用声明依赖并在运行时动态加载。多个应用可共享React、Vue等公共依赖,避免重复打包。

与single-spa等方案相比,Module Federation在构建层面实现模块共享,支持Tree-shaking和按需加载,且不依赖额外运行时框架。

Remote应用配置

创建一个独立的产品管理子应用,暴露ProductList组件和ProductService工具类:

// remote/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const deps = require('./package.json').dependencies;

module.exports = {
  entry: './src/index.js',
  mode: 'development',
  devServer: {
    port: 3001,
    headers: { 'Access-Control-Allow-Origin': '*' },
  },
  output: {
    publicPath: 'http://localhost:3001/',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: { presets: ['@babel/preset-react'] },
        },
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'productApp',
      filename: 'remoteEntry.js',
      exposes: {
        './ProductList': './src/components/ProductList',
        './ProductService': './src/services/ProductService',
      },
      shared: {
        react: { singleton: true, requiredVersion: deps.react },
        'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
      },
    }),
  ],
};

关键配置项说明:

  • name: 'productApp':远程应用标识,Host通过此名称引用
  • filename: 'remoteEntry.js':暴露入口文件,Host加载此文件获取远程模块
  • exposes:声明对外暴露的模块映射
  • shared:共享依赖配置,singleton: true确保全局只加载一个React实例

Host应用配置与远程模块加载

// host/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const deps = require('./package.json').dependencies;

module.exports = {
  devServer: { port: 3000 },
  plugins: [
    new ModuleFederationPlugin({
      name: 'shellApp',
      remotes: {
        productApp: 'productApp@http://localhost:3001/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, requiredVersion: deps.react },
        'react-dom': { singleton: true, requiredVersion: deps['react-dom'] },
      },
    }),
  ],
};

在Host应用中使用React.lazy动态加载远程组件,配合Suspense处理加载状态:

// host/src/App.jsx
import React, { Suspense, lazy } from 'react';

const ProductList = lazy(() => import('productApp/ProductList'));

function App() {
  return (
    <div>
      <h1>主应用 - 控制台</h1>
      <Suspense fallback={<div>加载产品模块...</div>}>
        <ProductList />
      </Suspense>
    </div>
  );
}

export default App;

Host入口文件需引入联邦运行时,确保共享依赖初始化:

// host/src/index.js
import('bootstrap.js');

// host/src/bootstrap.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

将入口拆分为index.js和bootstrap.js是Module Federation的必要步骤。webpack需要先加载联邦运行时再执行应用代码,否则共享依赖无法正确初始化。

共享依赖版本协商机制

多应用共享同一依赖时,Module Federation通过版本协商确定加载哪个版本。配置项控制协商行为:

shared: {
  react: {
    singleton: true,        // 全局只加载一个实例
    requiredVersion: '^18.0.0',
    eager: false,           // 异步加载共享依赖
    strictVersion: false,   // 允许使用不满足requiredVersion的版本
  },
  'react-dom': {
    singleton: true,
    requiredVersion: '^18.0.0',
  },
  lodash: {
    eager: true,            // 同步加载,不参与按需
    singleton: false,       // 允许多版本共存
  },
}

版本协商规则:当singleton: true时,所有应用共享最高满足条件的版本;当strictVersion: true时,版本不匹配会抛出警告;eager: true表示依赖不参与异步加载,直接打包进chunk。

独立部署与动态Remote配置

生产环境中Remote应用独立部署到CDN,Host应用通过环境变量配置Remote地址。动态注册Remote避免硬编码URL:

// host/src/utils/loadRemote.js
const remoteRegistry = {
  productApp: process.env.PRODUCT_APP_URL || 'https://cdn.example.com/product/remoteEntry.js',
  userApp: process.env.USER_APP_URL || 'https://cdn.example.com/user/remoteEntry.js',
};

async function loadRemoteEntry(url, scope) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = () => {
      const container = window[scope];
      container.init(__webpack_share_scopes__.default);
      resolve(container);
    };
    script.onerror = reject;
    document.head.appendChild(script);
  });
}

export async function importRemote(scope, module) {
  const url = remoteRegistry[scope];
  await loadRemoteEntry(url, scope);
  const factory = await window[scope].get(module);
  return factory();
}

使用动态加载函数替代静态import,运行时按需加载远程模块:

const ProductList = lazy(() => importRemote('productApp', './ProductList'));

部署时各子应用独立构建发布到CDN,Host应用更新环境变量即可切换Remote版本,无需重新构建Host。配合版本化CDN路径实现灰度发布:旧版Remote保持可用,新版逐步切换流量。组件库设计中,Module Federation支持跨应用共享UI组件而无需发布npm包,缩短了组件迭代到上线的交付链路。响应式布局的样式和逻辑也可以通过Remote按需加载,避免主应用打包体积膨胀。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/webpack5modulefederation-wei-qian-duan-shi-zhan-yuan-cheng/

(0)
小编小编
上一篇 1天前
下一篇 1天前

相关推荐