webpack高级应用篇(十三):模块联邦(Module Federation)- 未来组件包更新解决方案

目录

    • 两个独立的应用
      • APP1
      • APP2
    • 使用模块联邦
      • APP2
      • APP1


续篇:基于 Promise 的动态 Remote,让模块联邦版本化


Webpack 5 增加了一个新的功能 “模块联邦”,它允许多个 webpack 构建一起工作。 从运行时的角度来看,多个构建的模块将表现得像一个巨大的连接模块图。 从开发者的角度来看,模块可以从指定的远程构建中导入,并以最小的限制来使用。

多个独立的构建可以组成一个应用程序,这些独立的构建之间不应该存在依赖关系,因此可以单独开发和部署它们。这通常被称作微前端,但并不仅限于此。

Webpack5 模块联邦让 Webpack 达到了线上 Runtime 的效果,让代码直接在项目间利用 CDN 直接共享,不再需要本地安装 Npm 包、构建再发布了! 我们知道 Webpack 可以通过 DLL 或者 Externals 做代码共享时 Common Chunk, 但不同应用和项目间这个任务就变得困难了,我们几乎无法在项目之间做到按需热插拔。

模块联邦可以将一个应用的包应用于另一个应用,同时具备整体应用一起打包的公共依赖抽取能力。


以下为案例应用,详解见注释

两个独立的应用

APP1

目录

├── src
│   ├── Home.js
│   └── index.js
└── webpack.config.js

src/Home.js

export default (num) => {
  let str = '
    '; for (let i = 0; i < num; i++) { str += `
  • item ${i}
  • `
    ; } str += '
'
; return str; };

src/index.js

import Home from './Home';

const home = document.createElement('h1');
home.textContent = '这里是 App1 的 Home 模块:';
document.body.appendChild(home);
document.body.innerHTML += Home(5);

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  devServer: {
    port: '3000',
  },
  plugins: [
    new HtmlWebpackPlugin(),
  ],
};

执行 npx webpack serve

webpack高级应用篇(十三):模块联邦(Module Federation)- 未来组件包更新解决方案_第1张图片



APP2

目录

├── src
│   ├── Header.js
│   └── index.js
└── webpack.config.js

src/Header.js

export default () => {
  const header = document.createElement('h1');
  header.textContent = '这里是app2 的 Header 模块';
  return header;
};

src/index.js

import Header from './Header';

const div = document.createElement('div');
div.appendChild(Header());
document.body.appendChild(div);

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  devServer: {
    port: '3001',
  },
  plugins: [
    new HtmlWebpackPlugin(),
  ],
};

执行 npx webpack serve
webpack高级应用篇(十三):模块联邦(Module Federation)- 未来组件包更新解决方案_第2张图片



使用模块联邦

APP2

webpack.config.js

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

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  devServer: {
    port: '3001',
  },
  plugins: [
    new HtmlWebpackPlugin(),

    new ModuleFederationPlugin({
      // 模块联邦名字,提供给其他模块使用
      name: 'app2',
      // 提供给外部访问的资源入口
      filename: 'App2RemoteEntry.js',
      // 引用的外部资源列表
      remotes: {},
      // 暴露给外部的资源列表
      exposes: {
        /**
         *  ./Header 是让外部应用使用时基于这个路径拼接引用路径,如:App2/Header
         *  ./src/Header.js 是当前应用的要暴露给外部的资源模块路径
         */
        './Header': './src/Header.js',
      },
      // 共享模块,值当前被 exposes 的模块需要使用的共享模块,如lodash
      shared: {},
    }),
  ],
};

APP1

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

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  devServer: {
    port: '3000',
  },
  plugins: [
    new HtmlWebpackPlugin(),

    new ModuleFederationPlugin({
      // 模块联邦名字,提供给其他模块使用
      name: 'app1',
      // 提供给外部访问的资源入口
      filename: 'App1RemoteEntry.js',
      // 引用的外部资源列表
      remotes: {
        /**
         *  App2 引用其他应用模块的资源别名
         *  app2 是 APP2 的模块联邦名字
         *  http://localhost:3001 是 APP2 运行的地址
         *  App2RemoteEntry.js 是 APP2 提供的外部访问的资源名字
         *  可以访问到 APP2 通过 exposes 暴露给外部的资源
         */
        App2: 'app2@http://localhost:3001/App2RemoteEntry.js',
      },
      // 暴露给外部的资源列表
      exposes: {},
      // 共享模块,如lodash
      shared: {},
    }),
  ],
};

src/index.js

import Home from './Home';

/**
 *  需要异步导入
 *  App2 为 remotes 中定义的资源别名
 *  ./Header 为 APP2 exposes 定义的 ./Header
 */

import('App2/Header').then((Header) => {
  const body = document.createElement('div');
  body.appendChild(Header.default());
  document.body.appendChild(body);

  const home = document.createElement('h1');
  home.textContent = '这里是 App1 的 Home 模块:';
  document.body.appendChild(home);
  document.body.innerHTML += Home(5);
});

APP1 执行 npx webpack 可以看到 dist/main.js 包含了APP1的Header模块,它依然是通过CDN引入的:

APP1 执行 npx webpack serve 可以看到模块联邦使用成功!验证了模块联邦可以将一个应用的包应用于另一个应用,同时具备整体应用一起打包的公共依赖抽取的能力
webpack高级应用篇(十三):模块联邦(Module Federation)- 未来组件包更新解决方案_第3张图片

试想一下,你有一个组件包通过npm发布后,你的10个业务项目引用这个组件包。当这个组件包更新了版本,你的10个项目想要使用最新功能就必须一一升级版本、编译打包、部署,这很繁琐。但是模块联邦让组件包利用CDN的方式共享给其他项目,这样一来,当你到组件包更新了,你的10个项目中的组件也自然更新了。是不是很香(*^▽^*)



续篇:基于 Promise 的动态 Remote,让模块联邦版本化

源码:https://gitee.com/yanhuakang/webpack-demos/tree/master/advanced/step_13-Module-Feder
参考:webpack 5 模块联邦 Module Federation

你可能感兴趣的:(Webpack,前端工程化,webpack,模块联邦,Federation,模块联合,未来组件包更新解决方案)