在 grunt 构建中注入 ModuleFederationPlugin 配置

解读

国内前端项目普遍采用 webpack 作为最终打包器,而 Grunt 仅负责“任务编排”。
面试官真正想确认的是:

  1. 你能否把 webpack 5 的 ModuleFederationPlugin 这一“新基建”无缝嫁接到 存量 Grunt 流程
  2. 你是否理解 Grunt 与 webpack 的职责边界——Grunt 只做“驱动”,具体联邦配置仍由 webpack 完成;
  3. 你是否能在 不 eject、不改造原有 Gruntfile 风格 的前提下,用 最轻量的方式注入插件,并保证 多环境变量隔离构建缓存优化,以符合国内 CI/CD 规范。

知识点

  • grunt-webpack 官方插件的 webpack 5 适配版本(≥5.0.0)
  • ModuleFederationPluginname/remotes/shared/exposes 四元组语义
  • Gruntfile.js 的 grunt.initConfig 层级,以及 grunt.config.merge 运行时补丁机制
  • webpack.config 工厂函数(导出函数)与 grunt.template.process 结合,实现 <%= conf.xxx %> 变量替换
  • 国内镜像加速:在 shared 里强制 singletonstrictVersion,避免低版本重复下载
  • CI 场景下通过 process.env.APP_ENV 动态切换 publicPath,保证 cdn/oss 路径正确
  • Grunt watch 子进程webpack --watch端口隔离,防止 livereload 35729federation 热更新 3000 冲突

答案

  1. 安装依赖
    npm i -D webpack@5 webpack-cli grunt-webpack@5 module-federation-plugin

  2. Gruntfile.js 同级新建 webpack.federation.js

    const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
    const deps = require('./package.json').dependencies;
    
    module.exports = ({ production, publicPath }) => ({
      output: {
        publicPath,                 // 国内 OSS 路径
        uniqueName: 'microAppA',    // 微应用唯一命名
      },
      optimization: {
        runtimeChunk: false,        // 保证 remoteEntry 自包含
      },
      plugins: [
        new ModuleFederationPlugin({
          name: 'microAppA',
          filename: 'remoteEntry.js',
          exposes: {
            './Button': './src/components/Button',
          },
          shared: {
            ...deps,
            react: { singleton: true, strictVersion: true, requiredVersion: deps.react },
            'react-dom': { singleton: true, strictVersion: true, requiredVersion: deps['react-dom'] },
          },
        }),
      ],
    });
    
  3. Gruntfile.js 中注入

    module.exports = function (grunt) {
      const isProd = grunt.option('env') === 'prod';
      const publicPath = isProd ? 'https://cdn.xxx.com/microAppA/' : 'http://localhost:3001/';
    
      grunt.initConfig({
        webpack: {
          federation: (() => {
            const base = require('./webpack.federation.js')({
              production: isProd,
              publicPath,
            });
            return {
              mode: isProd ? 'production' : 'development',
              entry: './src/index',
              ...base,
            };
          })(),
        },
      });
    
      grunt.loadNpmTasks('grunt-webpack');
      grunt.registerTask('build:federation', ['webpack:federation']);
    };
    
  4. 运行
    npx grunt build:federation --env=prod
    产物 dist/remoteEntry.js 可直接上传 阿里云 OSS,并在 主应用 中通过 import('microAppA/Button') 消费。

拓展思考

  • 多子应用并行构建:利用 grunt-concurrentwebpack:federationAwebpack:federationB 并行跑,缩短 GitLab CI 总时长 40%
  • 灰度发布:在 shared.strategy 里增加 eager: truecustom 函数,按 cookie 版本号 动态降级;
  • 老项目渐进迁移:把 UMD 老 bundle 通过 ModuleFederationPlugin.exposes 暴露成 remote,让 React 18 新应用webpack 5 消费,实现 “灰度微前端”
  • 性能兜底:在 Gruntfile 里追加 grunt-contrib-compress 任务,对 remoteEntry.jsbrotli 压缩,结合 阿里云 CDN 自动回源,减少 首屏 120 KB
  • 合规审计:在 CI 阶段通过 grunt-eslint 扫描 exposes 路径,禁止 src/api/secret 目录被意外暴露,满足国内 金融客户安全评审 要求。