webpack

发布于: 7/26/2022 阅读大约需要2分钟

Module Federation - 模块联邦

Module Federation功能来自于 Webpack 5, 用于构建微前端
提供了远程加载应用模块的能力,前提是这些应用模块没有相互依赖,都是单独部署的。

传统的模块共享方案

  1. 将模块X发布到npm,然后在目标项目中进行install,当X组件发生更新变化是需要重新部署发布,并在目标项目中进行重新install升级。
  2. 使用iFrame进行模块嵌入,效果较好,但是使用iframe打开组件,每次都需要重新构建DOM树,导致复杂组件打开速度较慢,其次,iframe与外部应用跨应用通信也较为繁琐,在不同域名下更有可能会发生安全问题。

MF的使用

MF引用了HostRemote两个概念,分别代表当前应用和远程应用。
在这两个应用中的 webpack.config.js中都需要配置 ModuleFederationPlugin

Remote - 组件方配置

const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');

module.exports = {
  entry: './src/index',
  mode: 'development',
  devServer: {
    static: path.join(__dirname, 'dist'),
    port: 3002,
  },
  output: {
    publicPath: 'auto',
  },
  plugins: [
    new ModuleFederationPlugin({
      // 远程组件的应用名称
      name: 'app2',
      // 远程组件的入口文件
      filename: 'remoteEntry.js',
      // 定义需要导出的组件列表
      exposes: {
        './App': './src/App',
        './Component': './src/component',
      },
      // 可以共享的模块, singleton为单例,避免多项目使用不同实例导致问题
      shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
    }),
  ],
};

Host - 项目方配置

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;
const path = require("path");

module.exports = {
  entry: "./src/index",
  mode: "development",
  devServer: {
    static: path.join(__dirname, "dist"),
    port: 3001,
  },
  output: {
    publicPath: "auto",
  },
  plugins: [
    new ModuleFederationPlugin({
        // 当前应用名称
      name: "app1",
      // 远程应用加载js入口列表
      remotes: {
        app2: "app2@http://localhost:3002/remoteEntry.js",
      },
      //共享模块, 与远程模块配置相同
      shared: {react: { singleton: true }, "react-dom": { singleton: true }},
    })
  ],
};

然后在入口文件中或者需要使用远程模块的模块中进行异步加载该模块

const app2 = React.lazy(() => import('app2/App'))

MF存在的问题

  • 未提供隔离沙箱,可能会导致变量污染
  • 会存在CSS样式污染问题