Webpack中的模块联邦

1.简介

模块联邦可以真正实现跨应用间模块共享。它主要是去中心化的思想;npm组件库是中心化的思想。

模块联邦是Webpack5新内置的一个重要功能,可以让跨应用间真正做到模块共享模块联邦本身是一个普通的Webpack插件 ModuleFederationPlugin

2.什么是模块联邦

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

Webpack5 模块联邦让Webpack达到了线上Runtime的效果,让代码直接在项目间利用CDN直接共享,不再需要本地安装 Npm 包、构建再发布了!

我们知道 Webpack可以通过DLL或者Externals做代码共享时Common Chunk,但不同应用和项目间这个任务就变得困难了,我们几乎无法在项目之间做到按需热插拔。

  • NPM 方式共享模块
    想象一下正常的共享模块方式,对,就是 NPM。如下图所示,正常的代码共享需要将依赖作为Lib 安装到项目,进行Webpack打包。构建再上线,如下图:
    在这里插入图片描述
    对于项目 Home 与 Search,需要共享一个模块时,最常见的办法就是将其抽成通用依赖并分别安装在各自项目中。虽然 Monorepo 可以一定程度解决重复安装和修改困难的问题,但依然需要走本地编译。

  • UMD 方式共享模块
    真正 Runtime的方式可能是UMD方式共享代码模块,即将模块用Webpack UMD模式打包,并输出到其他项目中。这是非常普遍的模块共享方式:
    在这里插入图片描述
    对于项目Home与Search,直接利用UMD包复用一个模块。但这种技术方案问题。也很明显,就是包体积无法达到本地编译时的优化效果,且库之间容易冲突。

  • 微前端方式共享模块
    微前端:micro-frontends (MFE) 也是最近比较火的模块共享管理方式,微前端就是要解决多项目并存问题,多项目并存的最大问题就是模块共享,不能有冲突。
    在这里插入图片描述
    由于微前端还要考虑样式冲突、生命周期管理,所以本文只聚焦在资源加载方式上。微前端一般有两种打包方式:

    • 子应用独立打包,模块更解耦,但无法抽取公共依赖等。
    • 整体应用一起打包,很好解决上面的问题,但打包速度实在是太慢了,不具备水平扩展能力。
  • 模块联邦方式
    终于提到本文的主角了,作为 Webpack5 内置核心特性之一的 Federated Module:
    在这里插入图片描述
    从图中可以看到,这个方案是直接将一个应用的包应用于另一个应用,同时具备整体应用一起打包的公共依赖抽取能力。

3.参数

  • name 当前应用名称,需要全局唯一。
  • remotes 可以将其他项目的 name 映射到当前项目中。
  • exposes 表示导出的模块,只有在此申明的模块才可以作为远程依赖被使用。
  • shared 是非常重要的参数,制定了这个参数,可以让远程加载的模块对应依赖改为使用本地项目的ReactReactDOM

4.示例

本示例模拟三个应用,NavSearchHome。每个应用都是独立的,又通过模块联邦联系在了一块。

三个应用都要安装一些插件

npm init -y
npm webpack webpack-cli webpack-dev-server html-webpack-plugin -D

三个应用的目录结构如下:
在这里插入图片描述

4.1 nav导航应用

src/Header.js:

//用原生模拟一个组件。这里没用框架,也可以用框架写一个组件
const Header = () => {
    const header = document.createElement('h1')
    header.textContent = '公共头部内容'
    return header
}
export default Header

src/index.js:

//在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");
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  plugins: [
    new HtmlWebpackPlugin(),
    new ModuleFederationPlugin({
      // 模块联邦名字,其它项目引入时候要与此保持一致
      name: "nav",
      // 外部访问的资源名字,其它项目引入时候要与此保持一致
      filename: "remoteEntry.js",
      // 引用的外部资源列表
      remotes: {},
      // 暴露给外部资源列表
      exposes: {
        "./Header": "./src/Header.js",
      },
      // 共享模块,如lodash
      shared: {},
    }),
  ],
};

应用 webpack 运行服务:
在这里插入图片描述
在这里插入图片描述

4.2 home首页

src/HomeList.js:

const HomeList = (num) => {
    let str = '<ul>'
    for (let i = 0; i < num; i++) {
        str += '<li>item ' + i + '</li>'
    }
    str += '</ul>'
    return str
}
export default HomeList

src/index.js:

import HomeList from './HomeList'
//使用异步方式引入其它项目的组件
import('nav/Header').then((Header) => {
    const body = document.createElement('div')
    body.appendChild(Header.default())
    document.body.appendChild(body)
    document.body.innerHTML += HomeList(5)
})

webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  plugins: [
    new HtmlWebpackPlugin(),
    new ModuleFederationPlugin({
      name: "home",
      filename: "remoteEntry.js",
      remotes: {
        nav: "nav@http://localhost:3003/remoteEntry.js",
      },
      exposes: {
        "./HomeList": "./src/HomeList.js",
      },
      shared: {},
    }),
  ],
};

应用 webpack 运行服务:
在这里插入图片描述
在这里插入图片描述

4.3 search搜索

src/index.js:

Promise.all([import('nav/Header'), import('home/HomeList')])
    .then(([{
        default: Header
    }, {
        default: HomeList
    }]) => {
        document.body.appendChild(Header())
        document.body.innerHTML += HomeList(4)
    })

webpack.config.js

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  plugins: [
    new HtmlWebpackPlugin(),
    new ModuleFederationPlugin({
      name: "search",
      filename: "remoteEntry.js",
      remotes: {
        nav: "nav@http://localhost:3003/remoteEntry.js",
        home: "home@http://localhost:3001/remoteEntry.js"
      },
      exposes: {},
      shared: {},
    }),
  ],
};

应用 webpack 运行服务:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值