【微前端】手把手教你从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
相关文章
|
12天前
|
前端开发 JavaScript 架构师
了解微前端,深入前端架构的前世今生
该文章深入探讨了微前端架构的起源、发展及其解决的问题,并详细讲解了微前端在现代Web应用中的实现方式与优势,帮助读者理解微前端的设计理念和技术细节。
|
13天前
|
JSON 前端开发 JavaScript
不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识
该文章提供了Webpack的基础入门指南,涵盖安装配置、基本使用、加载器(Loaders)、插件(Plugins)的应用,以及如何通过Webpack优化前端项目的打包构建流程。
不会webpack的前端可能是捡来的,万字总结webpack的超入门核心知识
|
1月前
|
前端开发 开发者
在前端开发中,webpack 作为一个强大的模块打包工具,为我们提供了丰富的功能和扩展性
【9月更文挑战第1天】在前端开发中,Webpack 作为强大的模块打包工具,提供了丰富的功能和扩展性。本文重点介绍 DefinePlugin 插件,详细探讨其原理、功能及实际应用。DefinePlugin 可在编译过程中动态定义全局变量,适用于环境变量配置、动态加载资源、接口地址配置等场景,有助于提升代码质量和开发效率。通过具体配置示例和注意事项,帮助开发者更好地利用此插件优化项目。
70 13
|
14天前
|
前端开发 测试技术 API
探索微前端架构:构建现代化的前端应用
在软件开发中,传统单体架构已难以满足快速迭代需求,微前端架构应运而生。它将前端应用拆分成多个小型、独立的服务,每个服务均可独立开发、测试和部署。本文介绍微前端架构的概念与优势,并指导如何实施。微前端架构具备自治性、技术多样性和共享核心的特点,能够加速开发、提高可维护性,并支持灵活部署策略。实施步骤包括定义服务边界、选择架构模式、建立共享核心、配置跨服务通信及实现独立部署。尽管面临服务耦合、状态同步等挑战,合理规划仍可有效应对。
|
2月前
|
缓存 前端开发 JavaScript
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
Webpack 模块解析:打包原理、构造形式、扣代码补参数和全局导出
36 1
|
2月前
|
前端开发 JavaScript C++
【绝技大公开】Webpack VS Rollup:一场前端工程化领域的巅峰对决,谁能笑到最后?——揭秘两大构建神器背后的秘密与奇迹!
【8月更文挑战第12天】随着前端技术的发展,模块化与自动化构建成为标准实践。Webpack与Rollup作为主流构建工具,各具特色。Webpack是一款全能型打包器,能处理多种静态资源,配置灵活,适合复杂项目;Rollup专注于ES6模块打包,利用Tree Shaking技术减少冗余,生成更精简的代码。Rollup构建速度快,配置简洁,而Webpack则拥有更丰富的插件生态系统。选择合适的工具需根据项目需求和个人偏好决定。两者都能有效提升前端工程化水平,助力高质量应用开发。
28 1
|
2月前
|
前端开发 开发者
在前端开发中,webpack 作为模块打包工具,其 DefinePlugin 插件可在编译时动态定义全局变量,支持环境变量定义、配置参数动态化及条件编译等功能。
在前端开发中,webpack 作为模块打包工具,其 DefinePlugin 插件可在编译时动态定义全局变量,支持环境变量定义、配置参数动态化及条件编译等功能。本文阐述 DefinePlugin 的原理、用法及案例,包括安装配置、具体示例(如动态加载资源、配置接口地址)和注意事项,帮助开发者更好地利用此插件优化项目。
58 0
|
3月前
|
JSON 前端开发 JavaScript
前端工程化:Webpack配置全攻略
【7月更文挑战第14天】
55 6
|
2月前
|
前端开发 持续交付 开发者
探索现代前端开发中的微前端架构
微前端架构作为一种新兴的前端开发模式,旨在解决传统单体前端应用在可维护性和可扩展性方面的挑战。本文将深入探讨微前端的基本概念、实施方式以及其在提升团队协作效率和应用灵活性方面的优势。同时,我们还将探讨微前端架构的实现细节和常见的技术选型,以帮助开发者在实际项目中成功应用这一理念。
|
3月前
|
JSON 缓存 前端开发
前端工程化:Webpack配置全攻略
【7月更文挑战第18天】
46 1