モジュラーフェデレーション分析

Canglan.png

これは水を混ぜないで144番目のオリジナルです。よりオリジナルで良い記事を入手したい場合は、パブリックアカウントを検索してフォローしてください〜この記事は、Zhengcaiyunのフロントエンドブログで最初に公開されました:モジュールフェデレーションの分析

前書き

フロントエンドのパッケージングツールであるwebpackの重要なツールとして、誰もがこのプロジェクトに精通していると思います。少し前に、webpack5は新しい機能を導入しました:モジュールフェデレーション。聞いたことがあるかもしれませんが、プロジェクトでは使用されていません。今日は、実際の小さな戦闘でその使用法を理解していきます。

ビジネスシーン

会社にビジネスクラスターがあり、パブリックビジネスコンポーネントライブラリがアップグレードされていると仮定すると、ビジネスラインへの影響を最小限に抑え、基本コンポーネントライブラリバージョンをアップグレードするだけでビジネスライン全体をアップグレードできることが望まれます。次に、モジュールフェデレーションを使用してこれを実現することを検討できます。

彼とnpmパッケージを使用して実装されたソリューションの違いは、npmによってリリースされたコンポーネントライブラリが1.0.1から1.0.2にアップグレードされた場合、基幹業務プロジェクトを再構築、パッケージ化、およびリリースして、最新の機能モジュールフェデレーションにより、基幹業務プロジェクトをパッケージ化することなく、リアルタイムの動的更新が可能になります。

大まかなプロトタイプ図は次のとおりです。

project1のホームページのspecialItemとproject2のaboutページのsearchItemコンポーネントがproject2のホームページで使用されており、project2のaboutがproject1のaboutページで直接使用されていることがわかります。

全体的なソースコードは、モジュールフェデレーションのサンプルコードからのものですが、わずかな変更が加えられています。

以下は、変更されたディレクトリ構造の主要部分のみを示しており、冗長ファイルは省略されています。このプロジェクトのコードサンプルアドレスを確認するために私を突いてください。

├── README.md
├── app-exposes
│   ├── babel.config.js
│   ├── src
│   │   ├── App.vue
│   │   ├── assets
│   │   ├── components
│   │   │   ├── SearchItem.vue  ---搜索组件
│   │   │   └── SpecialItem.vue  ---自定义业务组件
│   │   ├── index.ts
│   │   ├── main.ts
│   │   ├── router
│   │   │   └── index.ts
│   │   └── views
│   │       ├── AboutView.vue   ---关于页
│   │       └── HomeView.vue  ---首页
│   ├── tsconfig.json
│   └── vue.config.js
├── app-general
│   ├── babel.config.js
│   ├── src
│   │   ├── router
│   │   │   └── index.ts
│   │   └── views
│   │       └── HomeView.vue
│   ├── tsconfig.json
│   └── vue.config.js
复制代码

足場を使用して、app-exposeとapp-generalのvue3プロジェクトをそれぞれ作成します。誰もがこの部分に精通している必要があり、ここではスキップします。面倒な方は、私が提供するデモサンプルを直接ご利用いただけます。

首先克隆本项目代码地址后,分别在 app-exposes 与 app-general 项目下执行 npm i 安装依赖,然后分别执行 npm run serve 运行代码。 此时能够看到本地起了两个服务,端口号分别为 8083 与 8081,其中 app-exposes 为 8083,app-general 为 8081。

项目运行示意效果图如下

然后我们看看两个项目的配置文件如何配置的。

app-exposes 的 vue.config.js 配置:

app-general 的 vue.config.js 配置:

可以看到,总体上我们用到了 webpack 原生的插件 ModuleFederationPlugin 来实现模块联邦的效果的。

在首页中,我们异步引用的 app-exposes 提供的 SearchItem 以及 SpecialItem 组件。

在 about 页面的路由配置中,我们直接引入的远程连接的 AboutView 页面。

如果想查看更多关于联邦模块的案例,可以访问官方仓库

二.联邦模块插件的结构及其常见的调用方式(Module Federation Plugin)

上面我们大概了解了下模块联邦插件的大致使用方法。不过知其然也要知其所以然,所以我接下来从个人角度简单聊一聊他的实现原理。

webpack 的整体流程上来说大体分为三个主要阶段

  • 初始化阶段
  • 构建阶段
  • 生成阶段

在这三大阶段时拥有极其庞大的插件库在各个阶段以及节点中发挥各自的作用,而模块联邦插件就是其中之一。

模块联邦作为一个 webpack5 时期新出的插件,形态上看通常是一个带有 apply 方法的类。

class ModuleFederationPlugin {
    apply(compiler) {}
}
复制代码

参数 compiler 是 webpack 上下文,可以调用 hook 对象注册各种钩子回调。

如下文中的 compiler.hooks.thisCompilation.tap,表明调用 afterPlugins 这个钩子的 tap 方法,传入插件名称与回调函数,执行我们指定的逻辑,webpack 通过这种方式来构建其庞大繁杂的插件体系。

class ModuleFederationPlugin {
    apply(compiler) {
        compiler.hooks.afterPlugins.tap("ModuleFederationPlugin", () => {
          ...
        }
    }
}
复制代码

钩子的核心逻辑定义在 Tapable 仓库,内部定义了如下类型的钩子。

const {
    SyncHook,
    SyncBailHook,
    SyncWaterfallHook,
    SyncLoopHook,
    AsyncParallelHook,
    AsyncParallelBailHook,
    AsyncSeriesHook,
    AsyncSeriesBailHook,
    AsyncSeriesWaterfallHook
 } = require("tapable");
复制代码

三.联邦模块的原理分析

联邦模块有两个主要概念:Host(消费其他 Remote)和 Remote(被 Host 消费)。 每个项目可以是 Host 也可以是 Remote,也可以两个都是。可以通过 webpack 配置来区分,可以参考例子

  • 作为 Host 需要配置 remote 列表和 shared 模块。
  • 作为 Remote 需要配置项目名(name),打包方式(library),打包后的文件名(filename),提供的模块(exposes),和 Host 共享的模块(shared)。

webpack 打包原理

webpack4 对于异步模块加载步骤

  • import(chunkId) => webpack_require.e(chunkId) 将相关的请求回调存入 installedChunks。
  • 发起 JSONP 请求。
  • 将下载的模块录入 modules。
  • 执行 chunk 请求回调。
  • 加载 module。
  • 执行用户回调。

联邦模块是基于 webpack 做的优化,所以在深入联邦模块之前我们首先得知道 webpack 是怎么做的打包工作。 webpack 每次打包都会将资源全部包裹在一个立即执行函数里面,这样虽然避免了全局环境的污染,但也使得外部不能访问内部模块。 在这个立即执行函数里面,webpack 使用 webpack_modules 对象保存所有的模块代码,然后用内部定义的 webpack_require 方法从 webpack_modules 中加载模块。并且在异步加载和文件拆分两种情况下向全局暴露一个 webpackChunk 数组用于沟通多个 webpack 资源,这个数组通过被 webpack 重写 push 方法,会在其他资源向 webpackChunk 数组中新增内容时同步添加到 webpack_modules 中从而实现模块整合。

联邦模块就是基于这个机制,修改了 webpack_require 的部分实现,在 require 的时候从远程加载资源,缓存到全局对象 window["webpackChunk"+appName] 中,然后合并到 webpack_modules 中。

ModuleFederationPlugin 的原理

源码中 ModuleFederationPlugin 主流程 主要做了三件事:

  • 通过参数是否配置 shared 来判断是否使用共享依赖 SharePlugin 模块。
  • 通过参数是否配置 exposes 来判断是否使用公开 ContainerPlugin 模块。
  • 通过参数是否配置 remotes 来判断是否使用 ContainerReferencePlugin 引用模块。

下面是项目源码,部分代码以及判断条件已省略。

// 源码目录 lib/container/ModuleFederationPlugin
class ModuleFederationPlugin {
  ...
	apply(compiler) {
		if (library && ...) {
			compiler.options.output.enabledLibraryTypes.push(library.type);
		}
		compiler.hooks.afterPlugins.tap("ModuleFederationPlugin", () => {
			if (options.exposes && ...) {
				new ContainerPlugin({
					...
				}).apply(compiler);
			}
			if (options.remotes && ...) {
				new ContainerReferencePlugin({
					remoteType,
					remotes: options.remotes
				}).apply(compiler);
			}
			if (options.shared) {
				new SharePlugin({
					shared: options.shared,
					shareScope: options.shareScope
				}).apply(compiler);
			}
		});
	}
}

module.exports = ModuleFederationPlugin;
复制代码

webpack5 模块联邦对异步模块加载的处理

  • 下载并执行 remoteEntry.js,挂载入口点对象到 window.app-exposes,他有两个函数属性,init 和 get。init 方法用于初始化作用域对象 initScope,get 方法用于下载 moduleMap 中导出的远程模块。
  • 加载 app-exposes 到本地模块。
  • 创建 app-exposes.init 的执行环境,收集依赖到共享作用域对象 shareScope。
  • 执行 app-exposes.init,初始化 initScope。
  • 用户 import 远程模块时调用 app-exposes.get(moduleName) 通过 Jsonp 懒加载远程模块,然后缓存在全局对象 window['webpackChunk' + appName]。
  • 通过 webpack_require 读取缓存中的模块,执行用户回调。

四.使用场景

目前模块联邦已经在微前端领域发挥了巨大的作用,也起到 webpack 能够越来越强大。

利用模块联邦强大的跨应用级模块共享能力,我们可以搭建一个非业务的中台搭建系统,实现 app 级别的低代码搭建平台,这与市场上常见页面级低代码搭建不同,能够实现系统级能力复用的同时降低维护成本。后续比如说 sso 单点登录,页面跳转,埋点,异常捕获等都可以考虑抽象封装成系统内置的方法到里面。

总结 通过这篇文章,我们收获了

  • 模块联邦的基础概念。
  • 模块联邦常用的配置项。
  • 通过简易配置实现雏形项目开发。
  • 模块联邦的基本原理。

参考文章

推荐阅读

性能优化——图片压缩、加载和格式选择

如何基于 WebComponents 封装 UI 组件库

Web Worker

如何落地一个智能机器人

一名练习时长 2 年零 8 个月的前端练习生自述

开源作品

  • 政采云前端小报

开源地址 www.zoo.team/openweekly/ (小报官网首页有微信交流群)

  • 商品选择 sku 插件

开源地址 github.com/zcy-inc/sku…

招贤纳士

政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 60 余个前端小伙伴,平均年龄 27 岁,近 4 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推动并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。

変えたいのなら、物事に夢中になり、物事を投げ始めたいと思っています。変えたいのなら、もっとアイデアが必要だと言われていますが、ゲームを中断することはできません。変えたいのなら、あなたにはその結果を出す能力がありますが、あなたはあなたを必要としません。あなたが達成したいことを変えたいのなら、あなたはサポートするチームが必要ですが、あなたが人々を導く場所はありません。確立されたリズムを変えたいなら「5年の作業時間と3年の作業経験」になります;オリジナルを変えたいなら理解は良いですが、窓の紙のその層のぼやけは常にあります。信念の力を信じるなら、普通の人は並外れたことを成し遂げることができ、彼らはより良い自己に出会うことができると信じてください。事業が軌道に乗る過程に参加し、事業に対する深い理解、健全な技術システム、価値を生み出す技術、波及効果を備えたフロントエンドチームの成長を個人的に促進したいのであれば、私は思います我々は話をする必要があります。いつでも、あなたが何かを書くのを待って、それをに送ってください[email protected]

おすすめ

転載: juejin.im/post/7101457212085633054