Webpack ModuleFederation 模块联邦实现机制
2023-1-10
| 2023-4-6
0  |  0 分钟
password
Created
Mar 7, 2023 11:54 AM
type
Post
status
Invisible
date
Jan 10, 2023
slug
summary
tags
Webpack
category
原理
icon

1. 是什么

多个独立的构建可以形成一个应用程序。这些独立的构建不会相互依赖,因此可以单独开发和部署它们。 这通常被称为微前端,但并不仅限于此。
鉴于mf的能力,我们可以完全实现一个去中心化的应用部署群:每个应用是单独部署在各自的服务器,每个应用都可以引用其他应用,也能被其他应用所引用,即每个应用可以充当host的角色,亦可以作为remote出现,无中心应用的概念。
对于微前端来讲, MF相当于是基于webpack的能力, 将chunk进行了模块化处理和装载, 而类似于qiankun等方案, 则是将应用进行模块化和动态化处理. 二者在实现上有比较大的差异
qiankun等方案的好处是, 老应用可以通过改造来进行接入, 但MF无法做到, 但从未来来讲, 基于MF的微前端方案因为能够实现chunk级别的控制, 理论上是一个更好的方案

2. 使用示例

生产端:
// STEP1: 配置MF的暴露和共享模块规则 const { ModuleFederationPlugin } = require('webpack').container; new ModuleFederationPlugin({ // 输出的模块名 name: 'app2', // 构建输出的文件名 filename: 'remoteEntry.js', // 被远程引用时可暴露的资源路径及其别名 exposes: { './App': './src/App', }, // 与其他应用之间可共享的依赖 shared: { react: { singleton: true }, 'react-dom': { singleton: true } }, }) // STEP2: 设置动态启动项目(使用动态的import) import("./bootstrap"); // STEP3: 项目正常运行 ...
消费端:
// STEP1: 配置MF的引入规则和远端模块地址 const {ModuleFederationPlugin} = require("webpack").container; new ModuleFederationPlugin({ // 输出的模块名 name: "app1", // 远程引用的应用名及其别名的映射 remotes: { // 远程引用的路径 app2: "app2@[app2Url]/remoteEntry.js", }, // 与其他应用之间可共享的依赖 shared: {react: {singleton: true}, "react-dom": {singleton: true}}, }) // STEP2: 动态启动项目, 并设置app2远程路径 window.app2Url = "http://localhost:3002" // 演示需要, 这个应该是判断环境进行动态的 import("./bootstrap"); // STEP3: 使用远端模块 import React, {Suspense} from "react"; const RemoteApp = React.lazy(() => import("app2/App")); const App = () => { return ( <Suspense fallback={"loading..."}> <RemoteApp/> </Suspense> ) } export default App;

3. 实现核心

在webpack4中, 我们可以使用require.ensure或动态import来在浏览器运行时引入异步的chunk来减小首屏加载数据大小
也就是说, 在webpack4中, 模块只有两种: 同步模块和异步模块
而在webpack5中, 因为MF的存在, 而MF是支持shared机制来共享第三方依赖, 以及暴露和引入MF模块, 使得原有的同步和异步模块变得不一样, 又衍生出了两种模块: 同步共享模块和异步远端模块

1. 同步共享模块

用于解决一个微前端系统的多个子系统之间依赖共享的问题,remote应用可以直接复用host应用上已经加载的共享模块,而不需要再次下载。 它跟externals很像,但是更安全更强大。
例如: 我们可以将react在MF中设置为共享模块
new ModuleFederationPlugin({ shared: { react: { 一些配置 } }})

2. 异步远端模块

host应用可以通过异步加载的方式使用的remote应用暴露出的模块。
import('remote-app/async-component')

3. 模块执行时机

  1. require模块并执行(第一个模块是入口模块),执行过程中
  1. 如果遇到静态import
  1. 如果这个模块是非共享同步模块,回到1如果这个模块是共享同步模块,前往4
  1. 如果遇到动态import
  1. 如果这个模块是本地异步模块,前往4如果这个模块是远端异步模块,也前往4
  1. 先require.ensure把异步模块相关的异步模块加载到modulesMap
  1. 如果是来自2.b,会初始化shared-scope,用来处理依赖共享相关逻辑如果是来自3.a,没有额外操作如果是来自3.b,会利用shared-scope来复用依赖,实现依赖共享
也就是说,即便在MF中,不管是共享模块还是远端模块,其实还是使用的require.ensure去加载一些异步chunk罢了。只不过稍有不同的是,因为牵扯到依赖共享的逻辑,会有一个shared-scope的概念,用来实现依赖共享的相关逻辑。
到这里,我们可以看到,MF的实现其实并没有魔法,仅仅是异步chunk罢了。这整个过程跟webpack5是没有绑定关系的,也就是说MF并非webpack5的专属功能,Rollupwebpack4都可以实现MF。

4. 实现原理

  1. 动态启动
动态启动的原因是需要前置拿到共享的chunk以保证后续内容能正常运行
  1. 加载依赖项
根据当前所需的共享依赖版本来进行依赖项加载(其实就是加载了本地的共享依赖版本), 以保证后续流程正常进行.
同时, 会将当前本地所使用的共享依赖, 以版本号为key注册到sharedScope中
  1. 下载远程依赖项定义的入口文件
下载远程依赖项入口文件, 并执行其init方法
同样的, 会将远程的共享依赖项, 以版本号为key, 注册到sharedScope中(如果两者版本号不一致的情况下)
这样做的好处就是在微前端场景下, 使用不同版本依赖包的组件等, 也能够保证其能使用预定的依赖版本号安全运行
  1. 加载远程expose的内容
根据入口文件, 对远程expose的内容进行加载和装载, 以便项目中可以通过远程地址加组件名称的方式来进行使用
  1. 继续后续流程
原理
  • Webpack
  • CentOS安装redisRxjs和它的一些概念
    目录