【微前端】手把手教你从0到1实现基于Webpack5 模块联邦(Module Federation)的微前端~(上)

简介: 【微前端】手把手教你从0到1实现基于Webpack5 模块联邦(Module Federation)的微前端~(上)

【微前端】在造一个微前端轮子之前,你需要知道这些~ 文中,我详细列举了当前实现微前端的多种方式,本文将基于 Webpack 5 Module Federation 从0到1实现一个以 React 项目为容器,集成多个 ReactVue 项目的微前端项目,文末有源码,请自行食用 ~

Good frontend development is hard. Scaling frontend development so that many teams can work simultaneously on a large and complex product is even harder.” —— Martin Fowler

好的前端开发很难。扩展前端开发,让许多团队可以同时在一个大型复杂的产品上工作,这就更难了。” —— 马丁·福勒

1、什么是模块联邦(Module Federation)

模块联邦(Module Federation)出现之前,独立应用程序之间共享代码的可扩展解决方案,最接近的是 externalsDLLPlugin,强制集中依赖于外部文件。共享代码很麻烦,单独的应用程序并不是真正独立的,通常共享的依赖项数量有限。此外,在单独捆绑的应用程序之间共享实际功能代码或组件是不可行的、低效的且收益甚微的。

模块联邦(Module Federation)出现的动机:多个独立的构建可以组成一个应用程序,这些独立的构建之间不应该存在依赖关系,因此可以单独开发和部署它们。这通常被称作微前端,但并不仅限于此。—— module federation —— webpack 官方

为什么会出现这个玩意?模块联邦的作者做了详细的说明:传送门,官方还给出了一个极简的微前端 demo

Webpack模块联邦(Webpack Module Federation)Webpack 5 中引入的一项新功能,它允许不同的 Webpack构建 之间共享代码并动态加载依赖项。具体来说,它允许将应用程序拆分成多个独立的 Webpack构建(或称为远程应用程序),这些构建可以在运行时共享代码和依赖项。

通过使用 Webpack模块联邦,不同的团队可以独立地构建和部署其应用程序的各个部分,而不必将所有代码都打包到一个大的 JavaScript 文件中。这可以提高应用程序的性能和可维护性,同时使得不同团队之间的合作更加容易。

image.pngWebpack模块联邦包含两个主要概念:提供者(Provider)使用者(Consumer)。提供者是将模块暴露给其他应用程序的 Webpack 构建,而使用者则是从提供者加载模块的 Webpack构建。使用 Webpack模块联邦 时,提供者将暴露一个或多个模块,这些模块可以由使用者在运行时动态加载和使用。

要使用Webpack模块联邦,需要在Webpack配置中添加相应的插件和配置。对于提供者,需要使用ModuleFederationPlugin插件配置要暴露的模块和提供者的名称。对于使用者,需要使用remoteEntry配置和remotes选项指定要从哪个提供者加载模块。

Webpack模块联邦是一项强大的功能,可以使得多个Webpack构建之间实现代码共享和动态加载,从而提高应用程序的性能和可维护性,同时使得不同团队之间的合作更加容易。

1.1 Module Federation 核心概念

  • Container

一个使用 ModuleFederationPlugin 构建的应用就是一个 Container,它可以加载其他的 Container,也可以被其他的 Container 加载。

  • Host&Remote

从消费者和生产者的角度看 ContainerContainer 可以分为 HostRemoteHost 作为消费者,他可以动态加载并运行其他 Remote 的代码,Remote 作为提供方,他可以暴露出一些属性、方法或组件供 Host 使用,这里要注意的一点是一个 Container 既可以作为 Host 也可以作为 Remote

  • Shared

shared 表示共享依赖,一个应用可以将自己的依赖共享出去,比如 reactreact-dommobx等,其他的应用可以直接使用共享作用域中的依赖从而减少应用的体积。

其运行原理可看以下这几篇优质的文章,这里就不赘述了,否则又是一篇“万字长文”:

1.2 配置属性

下面是 Module Federation 的几个属性:

const HtmlWebpackPlugin = require("html-webpack-plugin"); 
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: '_app_one_remote',  // 当前APP作为remote暴露组件时的APP的名字
            library: 'app_one_remote', // 当前APP作为remote暴露组件时的library名字
            filename: 'remoteEntry.js',
            // 所有被暴露的组件会打包到这个文件中,同理使用者也需要从这里引入
            remotes: {  
                app_two: "app_two_remote",  
                app_three: "app_three_remote"  
            }, 
            // 定义该库作为host时可能要引用的remote
            exposes: {
                'AppContainer': './src/App'
            }, 
            // 定义该库作为remote时,要暴露出去的组件。左边是相对路径和组件名字(其他库使用时候),右边是该组件在本库内的路径
            shared: ["react", "react-dom","react-router-dom"] // 和引入的组件公用的dependency
        })
    ]
};

1.2.1 name

在 Module Federation 中,每个模块都有一个名称。该名称将用于标识模块,并确保其他应用程序可以正确地引用该模块。name 属性用于指定模块的名称。例如:

plugins: [
    new ModuleFederationPlugin({
      name: 'she1',
    })
  ]
};

1.2.2 filename

filename 属性用于指定将要生成的远程模块的文件名。在本地开发模式下,该文件将被提供给其他远程应用程序使用。例如:

plugins: [
  new ModuleFederationPlugin({
    filename: 'she1/remoteShop.js'
  })
]

1.2.3 exposes

exposes 属性用于指定要暴露的模块和其导出项。它允许其他应用程序通过远程加载器使用这些模块。例如:

plugins: [
  new ModuleFederationPlugin({
    name: "myModule",
    exposes: {
        HomeComponent: './projects/app1-home/src/app/home/home.component.ts',
        ShellModule: './projects/app1-home/src/app/shell/shell.module.ts'
    }
  })
]

1.2.4 remotes

remotes 属性用于指定需要从其他应用程序远程加载的模块。例如:

plugins: [
  new ModuleFederationPlugin({
     remotes: {
        ShellModule: 'ShellModule@http://localhost:4400/remoteHome.js'
     }
  })
]

1.2.5 shared

shared 属性用于指定需要共享的模块,它允许你共享公共模块运行所依赖的节点库。如果两个应用程序都使用了同一版本的模块,则可以使用 shared 属性来共享该模块,从而减少应用程序的大小和加载时间。例如:

plugins: [
  new ModuleFederationPlugin({
   shared: {
      react: { eager: true, singleton: true },
      "react-dom": { eager: true, singleton: true },
      "place-my-order-assets": {eager: true, singleton: true},
    }
  })
]

在这个例子中,两个应用程序共享 React 和 React DOM 模块,它们都是单例模式,只有一个实例在内存中。

shared 选项使用 sharedPlugin,它有自己的一套配置属性。这有助于管理库在共享范围内的共享方式。

需要知道的一些重要配置选项是:

  • eager: 允许 Webpack 直接包含共享包,而不是通过异步请求获取库。当 eager 设置为 时true,所有共享模块将与暴露的模块一起编译。
  • singleton: 在共享范围内只允许共享模块的单一版本。这意味着在任何情况下,页面上只会加载一个版本的包。如果一个范围已经有一个 @angular/core 的版本,并且导入的模块使用了不同版本的@angular/coreWebpack 将忽略新版本并使用范围中已经存在的版本。
  • StrictVersion: 允许 Webpack 在版本无效时拒绝共享模块。这在指定所需版本时很有用。
  • RequiredVersion: 此选项说明共享模块的所需版本。在 Webpack 官方文档中了解有关共享选项的更多信息。

1.3 Module Federation 的特点

1.3.1 支持在项目中直接导出某个模块,直接单独打包

目前,我们在跨项目/跨团队项目间复用时,主要用的方式还是以导出 npm包 为主,而npm包的抽离、发布、维护都需要一定的成本。而且当多个项目依赖同一个npm包时,若npm有升级,则所有依赖项目都要相应更新,然后重新发布。而且往往你在写某个逻辑的时候,可能并没有预想到后来有复用的可能,那么这个时候抽成npm包来复用还是比较麻烦的。

Module Federation 模块是可以在项目中直接导出某个模块,单独打包的,如下图:

image.png
这样就很灵活,在复用逻辑的时候可以做到尽可能对现有项目少改造,快速导出。

1.2.2 支持运行时加载

可以减少打包时的代码体积,使用起来和在同一个项目下无区别。

image.png
image.png

1.3.3 更小的加载体积

因为拆分打包,所以有了更小的加载体积,而且当前子系统已经下载的 chunk 可以被共享,如果可以复用,下一个子系统将不会再次下载。这也就具备了可以在项目运行时同步更新不同项目间的同一模块逻辑依赖且节约了代码构建成本,维护成本等。

1.3.4 其他

  1. 相比过去, externals 无法多版本共存,dll 无法共享模块,Module Federation 完美解决。
  2. 借助运行时动态加载模块的特性,可以做到更好的 A/B test
  3. Module Federation 可以和服务端渲染结合使用,也与 CDN 的边缘计算契合的很好,畅想一下,它还能结合 serverless 做按需编译的加载。

1.4 Module Federation 的缺点

  1. 对环境要求略高,需要使用 webpack5,旧项目改造成本大。
  2. 对代码封闭性高的项目,依旧需要做npm那一套管理和额外的拉取代码,还不如npm复用方便。
  3. 拆分粒度需要权衡,虽然能做到依赖共享,但是被共享的lib不能做tree-shaking,也就是说如果共享了一个lodash,那么整个lodash库都会被打包到shared-chunk中。虽然依赖共享能解决传统微前端的externals的版本一致性问题。
  4. webpack为了支持加载remote模块对runtime做了大量改造,在运行时要做的事情也因此陡然增加,可能会对我们页面的运行时性能造成负面影响。
  5. 运行时共享也是一把双刃剑,如何去做版本控制以及控制共享模块的影响是需要去考虑的问题。
  6. 远程模块 typing 的问题。

2、基于模块联邦实现微前端项目

2.1 创建三个前端项目

我们先使用 create-react-app 创建 3 个项目:containermicro-front-end-1micro-front-end-2

npx create-react-app container
npx create-react-app micro-front-end-1
npx create-react-app micro-front-end-2
相关文章
|
2月前
|
人工智能 前端开发 JavaScript
前端架构思考 :专注于多框架的并存可能并不是唯一的方向 — 探讨大模型时代前端的分层式微前端架构
随着前端技术的发展,微前端架构成为应对复杂大型应用的流行方案,允许多个团队使用不同技术栈并将其模块化集成。然而,这种设计在高交互性需求的应用中存在局限,如音视频处理、AI集成等。本文探讨了传统微前端架构的不足,并提出了一种新的分层式微前端架构,通过展示层与业务层的分离及基于功能的横向拆分,以更好地适应现代前端需求。
|
1月前
|
缓存 前端开发 JavaScript
前端性能优化:Webpack与Babel的进阶配置与优化策略
【10月更文挑战第28天】在现代Web开发中,Webpack和Babel是不可或缺的工具,分别负责模块打包和ES6+代码转换。本文探讨了它们的进阶配置与优化策略,包括Webpack的代码压缩、缓存优化和代码分割,以及Babel的按需引入polyfill和目标浏览器设置。通过这些优化,可以显著提升应用的加载速度和运行效率,从而改善用户体验。
58 6
|
1月前
|
缓存 监控 前端开发
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第26天】前端工程化是现代Web开发的重要趋势,通过将前端代码视为工程来管理,提高了开发效率和质量。本文详细对比了Webpack和Gulp两大主流构建工具的选择与配置优化,并提供了具体示例代码。Webpack擅长模块化打包和资源管理,而Gulp则在任务编写和自动化构建方面更具灵活性。两者各有优势,需根据项目需求进行选择和优化。
75 7
|
1月前
|
缓存 前端开发 JavaScript
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第27天】在现代前端开发中,构建工具的选择对项目的效率和可维护性至关重要。本文比较了Webpack和Gulp两个流行的构建工具,介绍了它们的特点和适用场景,并提供了配置优化的最佳实践。Webpack适合大型模块化项目,Gulp则适用于快速自动化构建流程。通过合理的配置优化,可以显著提升构建效率和性能。
52 2
|
1月前
|
前端开发 API UED
深入理解微前端架构:构建灵活、高效的前端应用
【10月更文挑战第23天】微前端架构是一种将前端应用分解为多个小型、独立、可复用的服务的方法。每个服务独立开发和部署,但共同提供一致的用户体验。本文探讨了微前端架构的核心概念、优势及实施方法,包括定义服务边界、建立通信机制、共享UI组件库和版本控制等。通过实际案例和职业心得,帮助读者更好地理解和应用微前端架构。
|
2月前
|
前端开发 API UED
拥抱微前端架构:构建灵活、高效的前端应用
【10月更文挑战第17天】微前端架构是一种将前端应用拆分为多个小型、独立、可复用的服务的方法,每个服务可以独立开发、部署和维护。本文介绍了微前端架构的核心概念、优势及实施步骤,并分享了业界应用案例和职业心得,帮助读者理解和应用这一新兴架构模式。
|
2月前
|
存储 监控 前端开发
掌握微前端架构:构建未来前端应用的基石
【10月更文挑战第12天】随着前端技术的发展,传统的单体应用架构已无法满足现代应用的需求。微前端架构通过将大型应用拆分为独立的小模块,提供了更高的灵活性、可维护性和快速迭代能力。本文介绍了微前端架构的概念、核心优势及实施步骤,并探讨了其在复杂应用中的应用及实战技巧。
|
2月前
|
缓存 前端开发 JavaScript
深入了解Webpack:模块打包的革命
【10月更文挑战第11天】深入了解Webpack:模块打包的革命
|
2月前
|
存储 监控 前端开发
掌握微前端架构:构建可扩展的前端应用
【10月更文挑战第6天】随着前端应用复杂性的增加,传统单体架构已难以满足需求。微前端架构通过将应用拆分为独立模块,提升了灵活性与可维护性。本文介绍微前端的概念、优势及实施步骤,包括定义边界、创建共享UI库、设置通信机制等,并探讨其在SPA扩展、大型项目模块化及遗留系统现代化中的应用。通过实战技巧如版本控制、配置管理和监控日志,帮助团队高效协作,保持应用灵活性。微前端架构为构建大型前端应用提供有效解决方案,适合希望提升项目可扩展性的开发者参考。
|
3月前
|
前端开发 JavaScript 架构师
了解微前端,深入前端架构的前世今生
该文章深入探讨了微前端架构的起源、发展及其解决的问题,并详细讲解了微前端在现代Web应用中的实现方式与优势,帮助读者理解微前端的设计理念和技术细节。