使用 Webpack 优雅的构建微前端应用

88 阅读4分钟

一、引言

在当今的前端开发领域,微前端架构越来越受到青睐,它允许将大型的前端应用拆分成多个小型、可独立开发、部署和维护的子应用,各个子应用能够协同工作,就像一个完整的单体应用一样。而 Webpack 作为强大的模块打包工具,其提供的 Module Federation(模块联邦)特性为构建微前端应用提供了非常优雅且高效的解决方案。本文将详细介绍如何利用 Webpack 的模块联邦来构建微前端应用,并附上代码示例方便理解。

二、Webpack 模块联邦基础概述

Webpack 5 引入的模块联邦允许在不同的 Webpack 构建之间共享代码,使得各个独立构建的应用(可以看作是微前端架构里的子应用)能够动态地加载彼此的模块,就好像这些模块是本地的一样。

其核心的配置是通过 ModuleFederationPlugin 插件来实现,这个插件在 Webpack 配置文件中承担了定义模块如何被导出以及从哪里导入等关键功能。

三、构建微前端应用的步骤及示例代码

(一)创建项目结构

首先,我们创建一个简单的项目目录结构来模拟微前端应用场景,示例结构如下:

micro-frontend-demo
├── app-1
│   ├── src
│   │   ├── index.js
│   │   └── utils.js
│   └── webpack.config.js
└── app-2
    ├── src
    │   ├── index.js
    │   └── main.js
    └── webpack.config.js

这里 app-1 和 app-2 可以看作是两个子应用,其中 app-1 会作为模块的提供方,向外暴露一些模块供其他应用使用,app-2 则作为模块的使用方,会去加载 app-1 暴露的模块。

(二)模块提供方(app-1)配置及代码

在 app-1 的 webpack.config.js 中,配置如下:

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

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: 'http://localhost:3001/', // 根据实际开发环境调整
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1', // 应用的唯一标识,很重要
      library: {
        type: 'var',
        name: 'app1',
      },
      filename: 'remoteEntry.js',
      exposes: {
        './utils': './src/utils.js', // 声明要暴露的模块路径
      },
    }),
  ],
  devServer: {
    port: 3001,
    static: {
      directory: path.join(__dirname, 'dist'),
    },
  },
};

在 app-1 的 src/utils.js 中,我们可以简单写一个示例函数:

export const add = (a, b) => a + b;

src/index.js 可以作为入口文件,简单引入这个模块(这里只是示例,实际可能有更复杂的逻辑启动应用等):

import { add } from './utils';
console.log(add(2, 3));

(三)模块使用方(app-2)配置及代码

在 app-2 的 webpack.config.js 中,配置如下:

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

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    publicPath: 'http://localhost:3002/', // 根据实际开发环境调整
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'app2',
      library: {
        type: 'var',
        name: 'app2',
      },
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js', // 指定从哪里加载远程模块
      },
    }),
  ],
  devServer: {
    port: 3002,
    static: {
      directory: path.join(__dirname, 'dist'),
    },
  },
};

在 app-2 的 src/main.js 中,我们可以去加载并使用 app-1 暴露的模块:

async function loadRemoteModule() {
  const { add } = await import('app1/utils');
  console.log(add(5, 6));
}

loadRemoteModule();

四、运行示例

  1. 首先分别在 app-1 和 app-2 的目录下,运行 webpack serve 命令来启动各自的开发服务器(前提是已经安装好 Webpack 及相关依赖)。
  2. 打开浏览器,访问 http://localhost:3002/(对应 app-2 的开发服务器地址),可以看到控制台会打印出 11,这说明成功从 app-1 加载并使用了 add 这个模块中的函数,也就验证了我们通过 Webpack 模块联邦构建微前端应用的可行性。

五、总结

通过上述示例可以看到,利用 Webpack 的模块联邦特性构建微前端应用,在配置和代码编写上是比较清晰且易于上手的。它为团队协作开发大型前端项目、实现各个子应用的独立演进提供了有力的支持。当然,在实际应用中,还需要考虑更多诸如样式隔离、共享依赖管理、通信机制等复杂的问题,但掌握了模块联邦的基础构建方式是迈出微前端实践的重要一步。

希望这篇文章及示例代码能够帮助您~~~~~实际项目中的配置和代码可能会根据具体需求更加复杂多样,需要开发者灵活调整和优化。