最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

微前端架構(gòu)ModuleFederationPlugin源碼解析

 更新時(shí)間:2022年11月01日 14:41:45   作者:echolc55873  
這篇文章主要為大家介紹了微前端架構(gòu)ModuleFederationPlugin源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

序言

本文是 Webpack ModuleFederationPlugin(后面簡(jiǎn)稱(chēng) MF) 源碼解析 文章中的第一篇,在此系列文章中,我將帶領(lǐng)大家抽絲剝繭、一步步地去解析 MF 源碼。當(dāng)然為了幫助大家理解,可能中間也會(huì)涉及到 Webpack 源碼中的其它實(shí)現(xiàn),我會(huì)根據(jù)情況或淺或深的一并進(jìn)行講解。因?yàn)榭?Webpack 源碼需要掌握的知識(shí)量非常大,所以為了更好理解文章中的內(nèi)容,你最好有如下 Webpack 相關(guān)的背景知識(shí):

  • 對(duì) Webpack 核心的數(shù)據(jù)結(jié)構(gòu):Dependency、Module、Chunk 等有基本的認(rèn)識(shí)
  • 了解 Webpack 中的插件機(jī)制,對(duì)基于 tabpable 的 Hooks 機(jī)制有一定的了解,如果寫(xiě)過(guò) Webpack 插件就更好了
  • 看過(guò) MF 的官方文檔,對(duì)其帶來(lái)的關(guān)鍵性作用有基本的認(rèn)識(shí),如果了解一些其應(yīng)用場(chǎng)景就更好了

話(huà)不多說(shuō),讓我們開(kāi)始正文。

背景

先簡(jiǎn)單說(shuō)一下為什么要去閱讀 MF 的源碼,我個(gè)人理解閱讀源碼有兩個(gè)原因:

一,它的實(shí)現(xiàn)非常優(yōu)秀,通過(guò)閱讀源碼能學(xué)習(xí)一些設(shè)計(jì)思想和編程技巧;

二,工作或者自己的項(xiàng)目使用到了,但是官方給的文檔不太夠,遇到問(wèn)題無(wú)論最后有沒(méi)有解決,都有點(diǎn)摸不著頭腦,閱讀源碼是為了更好地了解其內(nèi)部實(shí)現(xiàn),遇到問(wèn)題更容易 debug。

而我閱讀 MF 的源碼,主要是出于第二種目的,當(dāng)然我個(gè)人對(duì) Webpack 也是非常感興趣。目前我們部門(mén) B 端的產(chǎn)品是基于 MF 實(shí)現(xiàn)的微前端架構(gòu),而我主要負(fù)責(zé) B 端的開(kāi)發(fā)以及參與 B 端性能優(yōu)化專(zhuān)項(xiàng),今年大部分時(shí)間都是跟 MF “搏斗”。

雖然到目前為止,性能優(yōu)化已經(jīng)獲得了一些階段性的勝利,但是實(shí)際上在這個(gè)過(guò)程中,我們還是走了很多彎路。這些彎路不少是由于對(duì) MF 內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)不夠了解導(dǎo)致的,當(dāng)然除此之外,我們還需要建立一套規(guī)范的 MF 標(biāo)準(zhǔn)化開(kāi)發(fā)流程。所以,閱讀 MF 源碼對(duì)于我個(gè)人來(lái)說(shuō)非常有必要。

首先,我們先簡(jiǎn)單了解下 MF 相關(guān)知識(shí)。

MF 基本介紹

首先,MF 是一個(gè) Webpack 的官方插件,在 Webpack 生態(tài)中有茫茫多的插件中,好像一個(gè)插件有點(diǎn)微不足道。但是,MF 的作者稱(chēng)其為 “A game-changer in JavaScript architecture”,當(dāng)然從構(gòu)建工具的角度來(lái)講,有點(diǎn)言過(guò)其實(shí),因?yàn)樗荒苡糜?Webpack 中。

但是從它帶來(lái)的 JavaScript 架構(gòu)設(shè)計(jì)上的理念:遠(yuǎn)程依賴(lài)共享(復(fù)用組件或者其它邏輯), 我覺(jué)得其實(shí)是給前端帶來(lái)新的思考視角。

以前我們復(fù)用組件或者邏輯主要的方式有:

  • 抽離一個(gè) NPM 包,從維護(hù)性和復(fù)用性角度來(lái)講,是目前最常見(jiàn)的方式。缺點(diǎn)在于,在微前端架構(gòu)中,如果 fix 了一個(gè) NPM 包問(wèn)題,那么每一個(gè)應(yīng)用都需要升級(jí)版本,重新構(gòu)建打包部署上線(xiàn),多團(tuán)隊(duì)開(kāi)發(fā)的時(shí)候非常低效;
  • 將產(chǎn)物打包成 UMD 的格式,然后通過(guò) CDN 的方式能一定程度解決重新構(gòu)建打包上線(xiàn)的問(wèn)題,但是隨著復(fù)用的組件和邏輯越多,可能會(huì)引入很多多余的 chunk 問(wèn)題(如果對(duì)性能有很高的要求) 。比如 A 和 B 組件同時(shí)依賴(lài)了 lodash,那么打包成 UMD 格式有多余的 lodash chunk,沒(méi)法復(fù)用。

我們來(lái)看下 MF 是怎么解決這個(gè)問(wèn)題的。首先看一個(gè)簡(jiǎn)單的 MF 使用的例子,假設(shè)我們現(xiàn)在有兩個(gè)應(yīng)用 app1 和 app2:

// app1 webpack.config.js
module.exports = {
  // 省略其它配置
	plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      remotes: {
        app2: 'app2@http://localhost:3002/remoteEntry.js',
      },
      exposes: {
        './input': './src/components/Input'
      },
      shared: {
        'react': {
          singleton: true, 
          requiredVersion: require('./package.json').dependencies.react
        },
        'react-dom': {
          singleton: true,
          requiredVersion: require('./package.json').dependencies['react-dom']
        },
        'lodash': {
          requiredVersion: require('./package.json').dependencies['lodash'],
          singleton: true,
        }
      }
    }),
  ] 
}
// app1 src/components/Input.tsx
import * as React from 'react'
import { Input } from 'antd'
export default function WrapperInput () {
  return (
    <div>
      app1 input: <Input />
    </div>
  )
}
// app1 src/App.tsx
import { Input } from 'antd';
import * as React from 'react';
const RemoteButton = React.lazy(() => import('app2/Button'));
const App = () => (
  <div>
    <h1>Typescript</h1>
    <h2>App 1</h2>
    <React.Suspense fallback="Loading Button">
      <RemoteButton />
    </React.Suspense>
    <div>
      <Input />
    </div>
  </div>
);
export default App;    

app2 的部分代碼:

// app2 webpack.config.js
module.exports = {
  // 省略其它配置
  plugins: [
    new ModuleFederationPlugin({
      name: 'app2',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button',
      },
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
      },
  }),
}
// app2 src/Button.tsx
import * as React from 'react';
const Button = () => <button>App 3 Button</button>;
export default Button;
// app2 src/App.tsx
import * as React from 'react';
import LocalButton from './Button';
import RemoteInput from 'app1/input';
const App = () => (
  <div>
    <h1>Typescript</h1>
    <h2>App 3</h2>
    <LocalButton />
    <React.Suspense fallback={null}>
      <RemoteInput />
    </React.Suspense>
  </div>
);
export default App;

最后實(shí)現(xiàn)的效果:

簡(jiǎn)單的 Webpack 配置,我們就可以實(shí)現(xiàn) app1 和 app2 兩個(gè)應(yīng)用之間的組件遠(yuǎn)程共享,從代碼看,我們知道 app1 依賴(lài)了 app2 的 Button 組件,而 app2 依賴(lài)了 app1 的 Input 組件。

當(dāng)然不止如此,MF 還可以做到:

  • 依賴(lài)復(fù)用, app1 和 app2 同時(shí)依賴(lài)了 react 和 react-dom,那我們可以在雙方的 Webpack 配置中,將兩個(gè)依賴(lài)配置成 shared,而且通過(guò) requiredVersion 指定版本;
  • 微前端架構(gòu),微前端架構(gòu)有很多實(shí)現(xiàn)的方式,比如 iframe、web-component 等,但是 MF 的出現(xiàn),使得實(shí)現(xiàn)一套微前端的架構(gòu)更加簡(jiǎn)單,也能非常容易解決微前端架構(gòu)中的一些組件復(fù)用問(wèn)題、頻繁構(gòu)建部署上線(xiàn)問(wèn)題;
  • 支持服務(wù)端渲染,MF 的實(shí)現(xiàn)不依賴(lài)瀏覽器,同樣的代碼,只需要將 Webpack 配置中的target改成

node,那么構(gòu)建的產(chǎn)物就能支持 SSR。

到這里,讀者已經(jīng)對(duì) MF 的使用和定位有了基本的印象,根據(jù) MF 帶來(lái)的全新的復(fù)用能力,我們可以做一些應(yīng)用場(chǎng)景的思考。

應(yīng)用場(chǎng)景

微前端架構(gòu)

微前端是這幾年比較火的一個(gè)前端應(yīng)用架構(gòu)方案,其中比較核心的一點(diǎn)是各子應(yīng)用之間要做到獨(dú)立開(kāi)發(fā),獨(dú)立構(gòu)建部署上線(xiàn)。 從上一節(jié)對(duì) MF 的介紹中,我們發(fā)現(xiàn)它天然就已經(jīng)有這個(gè)優(yōu)勢(shì),因此為了設(shè)計(jì)一個(gè)基于 MF 的微前端架構(gòu),我們要解決的第一點(diǎn)是子應(yīng)用之間需要有個(gè)類(lèi)似中心化的服務(wù),將其它子應(yīng)用的服務(wù)地址下發(fā)給需要消費(fèi)的子應(yīng)用;第二點(diǎn),我們要解決子應(yīng)用之間的一些通信問(wèn)題,例如共享的一些用戶(hù)狀態(tài)。 當(dāng)然還有一些其它問(wèn)題,例如 UI 一致性問(wèn)題。

基于以上的問(wèn)題,我們可以很容易想到一種非常經(jīng)典的微前端架構(gòu)方案,那就是基于一個(gè)基座服務(wù)的中心化的架構(gòu)方式。

每個(gè) APP 都是一個(gè)子應(yīng)用,這里可以有兩種方式:如果完全不需要依賴(lài)基座的狀態(tài),則可以做成一個(gè)更加通用的前端服務(wù),只作為提供方,在 MF 中也稱(chēng)為 remotes 應(yīng)用。如果需要依賴(lài)主應(yīng)用的狀態(tài),或者說(shuō)只導(dǎo)出路由讓基座幫忙注冊(cè),這樣就可以共享基座的所有狀態(tài),這種方式與我們現(xiàn)在 B 端的架構(gòu)方式類(lèi)似。這樣的架構(gòu)方式,也能通過(guò) MF shared 機(jī)制鎖定 UI 庫(kù)的版本,保證所有子應(yīng)用 UI 的一致性。

服務(wù)化的 library 和 components

跳出微前端架構(gòu),假設(shè)我們現(xiàn)在的場(chǎng)景是維護(hù)一個(gè)巨型前端應(yīng)用,我們發(fā)現(xiàn)隨著頁(yè)面和依賴(lài)的第三方依賴(lài)逐漸增多,那么每次開(kāi)發(fā)構(gòu)建部署上線(xiàn)的時(shí)長(zhǎng)也會(huì)不斷增加。雖然 Webpack v5+ 版本已經(jīng)做了很多優(yōu)化例如本地緩存,但是對(duì)于巨型應(yīng)用,我們還是發(fā)現(xiàn)構(gòu)建還是非常低效。于是,基于 MF 的能力,我們可以做這樣的一個(gè)架構(gòu)設(shè)計(jì):

我們可以將平時(shí)使用的第三方庫(kù)和組件庫(kù),分別做成一個(gè)單獨(dú)的服務(wù),如果部門(mén)技術(shù)棧統(tǒng)一的項(xiàng)目可以通過(guò) MF 插件遠(yuǎn)程使用這兩個(gè)服務(wù),這樣無(wú)論是開(kāi)發(fā)時(shí)還是上線(xiàn)構(gòu)建都可以省掉這部分的構(gòu)建時(shí)間,一定程度上提高了開(kāi)發(fā)效率。

MF 的使用姿勢(shì)非常靈活,你可以根據(jù)開(kāi)發(fā)需要,充分挖掘更多的使用場(chǎng)景。MF 介紹的部分就到這里,下面我們正式進(jìn)入源碼解析的內(nèi)容。

ModuleFederationPlugin 源碼解析

入口源碼

MF 插件相關(guān)的源碼放在 lib/container 下,我們首先看下 lib/container/ModuleFedration.js的代碼:

// 省略一些 import 代碼
class ModuleFederationPlugin {
	/**
	 * @param {ModuleFederationPluginOptions} options options
	 */
	constructor(options) {
		validate(options);
		this._options = options;
	}
	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		const { _options: options } = this;
		// expose 模塊編譯產(chǎn)物導(dǎo)出的類(lèi)型,選項(xiàng)有 var、umd、commonjs、module 等,跟 output 配置中的 library 作用是一樣的
		// var 代表輸出的模塊是掛在 window 對(duì)象上
		const library = options.library || { type: "var", name: options.name };
		// remote 模的類(lèi)型,選項(xiàng)有 var、umd、commonjs、module 等,跟 output 配置中的 library 作用是一樣的
		const remoteType =
			options.remoteType ||
			(options.library && isValidExternalsType(options.library.type)
				? /** @type {ExternalsType} */ (options.library.type)
				: "script");
		// 	enabledLibraryTypes 專(zhuān)門(mén)存儲(chǔ) entry 需要輸出的 library 類(lèi)型,然后被 EnableLibraryPlugin 插件消費(fèi),
		if (
			library &&
			!compiler.options.output.enabledLibraryTypes.includes(library.type)
		) {
			compiler.options.output.enabledLibraryTypes.push(library.type);
		}
		// 在完成所有內(nèi)部插件注冊(cè)后處理 MF 插件
		compiler.hooks.afterPlugins.tap("ModuleFederationPlugin", () => {
			if (
				options.exposes &&
				(Array.isArray(options.exposes)
					? options.exposes.length > 0
					: Object.keys(options.exposes).length > 0)
			) {
				// 如果有 expose 配置,則注冊(cè)一個(gè) ContainerPlugin
				new ContainerPlugin({
					name: options.name,
					library,
					filename: options.filename,
					runtime: options.runtime,
					shareScope: options.shareScope,
					exposes: options.exposes
				}).apply(compiler);
			}
			if (
				options.remotes &&
				(Array.isArray(options.remotes)
					? options.remotes.length > 0
					: Object.keys(options.remotes).length > 0)
			) {
				// 如果有 expose 配置,則初始化一個(gè) ContainerReferencePlugin
				new ContainerReferencePlugin({
					remoteType,
					shareScope: options.shareScope,
					remotes: options.remotes
				}).apply(compiler);
			}
			if (options.shared) {
				// 如果有 shared 配置,則初始化一個(gè) SharePlugin
				new SharePlugin({
					shared: options.shared,
					shareScope: options.shareScope
				}).apply(compiler);
			}
		});
	}
}

從代碼中可以看出,MF 插件入口的代碼其實(shí)不復(fù)雜,核心的代碼不到 100 行,我們首先把焦點(diǎn)放在插件初始化的 options參數(shù)上,它的類(lèi)型為 ModuleFederationPluginOptions。

這里有個(gè)細(xì)節(jié)可以注意下,因?yàn)?Webpack 的源碼是用純 JS 寫(xiě)的,為了彌補(bǔ)如像 TypeScript 的類(lèi)型注釋的優(yōu)勢(shì)使得源碼更加可讀的問(wèn)題,Webpack 使用了 JSDoc 配合 VS Code,在大多數(shù)場(chǎng)景下也能起到類(lèi)型注釋的效果,而 Webpack 根目錄下的 declarations目錄使用了 TS 定義了核心的一些數(shù)據(jù)類(lèi)型,然后導(dǎo)出給其它 JS 文件在使用 JSDoc 時(shí)使用。

我們回到主題,我們看下ModuleFederationPluginOptions的類(lèi)型定義:

export interface ModuleFederationPluginOptions {
	/**
	 * container 應(yīng)用導(dǎo)出的模塊配置,一般是一個(gè)對(duì)象
	 */
	exposes?: Exposes;
	/**
	 * 打包產(chǎn)物的文件名稱(chēng)
	 */
	filename?: string;
	/**
	 * 構(gòu)建產(chǎn)物的類(lèi)型,里面的 type 配置可以是 umd、commonjs、var 等類(lèi)型
	 */
	library?: LibraryOptions;
	/**
	 * container 的名稱(chēng)
	 */
	name?: string;
	/**
	 * 依賴(lài)的 remote 應(yīng)用 library 類(lèi)型,配置的值可以是 umd、commonjs、script、var 等
	 */
	remoteType?: ExternalsType;
	/**
	 * container 應(yīng)用依賴(lài)的遠(yuǎn)程應(yīng)用
	 */
	remotes?: Remotes;
	/**
	 * 配置了該選項(xiàng),會(huì)為模塊split 一個(gè)以該名稱(chēng)命名的 chunk
	 */
	runtime?: EntryRuntime;
	/**
	 * 所有共享模塊的作用域名稱(chēng),默認(rèn)為 default,很少會(huì)修改
	 */
	shareScope?: string;
	/**
	 * 應(yīng)用之間需要共享的模塊
	 */
	shared?: Shared;
}

每個(gè)選項(xiàng)我都用注釋做了簡(jiǎn)單的介紹,我們重點(diǎn)關(guān)注幾個(gè)常用的配置,對(duì)于 library、runtime、

remoteType等配置平時(shí)很少使用,這里先不過(guò)多介紹,后面看到相關(guān)的源碼可以再回顧。

filenamename比較好理解,以上一小節(jié)的 app1 的 Webpack 配置為例,我們可以看到其配置如下:

   new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      // 省略其它配置
   })   

如果這樣配置,app1 正好expose了一些模塊給其它應(yīng)用消費(fèi),例如 app2,則 app2 首先要通過(guò) app1 的

name去找到它,也就是會(huì)在 remotes的配置中添加 app1 的指向,這里等會(huì)介紹 remotes選項(xiàng)時(shí)再細(xì)說(shuō)。而 app2 在運(yùn)行時(shí)就會(huì)加載到 app1 的構(gòu)建產(chǎn)物 remoteEntry.js,訪(fǎng)問(wèn) app2 的服務(wù),打開(kāi) network,我們可以看到其加載了 app1 的 remoteEntry.js

我們重點(diǎn)介紹下 exposesremotes、shared等選項(xiàng)。

在上面的 MF 插件源碼中,其核心的幾行代碼就是,在 afterPlugins hook觸發(fā)后(完成其它所有內(nèi)置插件初始化后),根據(jù)是否有上面三個(gè)配置,來(lái)決定是否要注冊(cè) ContainerPlugin、

ContainerReferencePlugin、SharePlugin等插件。所以更加核心的實(shí)現(xiàn),是分別交給了上面三個(gè)插件去完成。

Exposes

exposes的配置是告訴 Webpack 當(dāng)前應(yīng)用導(dǎo)出給其它應(yīng)用消費(fèi)的模塊,首先我們來(lái)看下 exposes配置的類(lèi)型定義:

export type Exposes = (ExposesItem | ExposesObject)[] | ExposesObject;
export type ExposesItem = string;
export type ExposesItems = ExposesItem[];
export interface ExposesObject {
	[k: string]: ExposesConfig | ExposesItem | ExposesItems;
}
export interface ExposesConfig {
	import: ExposesItem | ExposesItems;
	name?: string;
}

上面的類(lèi)型定義相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,只是套娃比較多,還是以上面 app1 的 Webpack 配置為例,據(jù)我平時(shí)了解到的,最常見(jiàn)的配置方式還是:

exposes: {
	'./input': './src/components/Input'
},

但是你也可以配置:

exposes: {
	'./input': {
    name: 'input',
    import: './src/components/Input'  
  }
},

這種方式配置會(huì)有什么不一樣了?這里會(huì)留一個(gè)懸念,在看后續(xù)的源碼中,我們?cè)僭敿?xì)介紹。

Remotes

remotes配置是告訴 Webpack 當(dāng)前應(yīng)用依賴(lài)了哪些遠(yuǎn)程應(yīng)用,我們來(lái)看下其類(lèi)型定義:

export type Remotes = (RemotesItem | RemotesObject)[] | RemotesObject;
export type RemotesItem = string;
export type RemotesItems = RemotesItem[];
export interface RemotesObject {
	[k: string]: RemotesConfig | RemotesItem | RemotesItems;
}
export interface RemotesConfig {
	/**
	 * 共享模塊需要依賴(lài)的其它模塊
	 */
	external: RemotesItem | RemotesItems;
	// 共享作用域的名稱(chēng),默認(rèn)為 default
	shareScope?: string;
}

還是以 app1 為例,我們回顧其 remote 的配置:

remotes: {
  app2: 'app2@http://localhost:3002/remoteEntry.js',
},

告訴了 Webpack 如果需要消費(fèi) app2 導(dǎo)出的模塊,那么則需要加載 app2 服務(wù)的 remoteEntry.js文件,所以 app1 在初始化的時(shí)候就會(huì)加載此文件,然后通過(guò)下面的方式加載 app2 導(dǎo)出的模塊:

import RemoteButton from 'app2/Button';

是不是有點(diǎn)神奇,這里面的實(shí)現(xiàn)用了什么黑魔法,簡(jiǎn)單的幾個(gè)配置,然后啟動(dòng)服務(wù),就能消費(fèi)其它遠(yuǎn)程應(yīng)用的模塊。保持耐心,后續(xù)我們將慢慢揭開(kāi)其神秘的面紗。

Shared

MF 關(guān)于 shared配置部分是我個(gè)人覺(jué)得最復(fù)雜的部分,當(dāng)然 SharedPlugin的實(shí)現(xiàn)也是相對(duì)來(lái)說(shuō)比較復(fù)雜,因?yàn)檫@里牽扯到一些需要 shared配置延伸出的例如單例問(wèn)題。 先留個(gè)懸念,稍后解釋單例問(wèn)題,我們還是先看 shared配置類(lèi)型定義:

export type Shared = (SharedItem | SharedObject)[] | SharedObject;
export type SharedItem = string;
export interface SharedObject {
	[k: string]: SharedConfig | SharedItem;
}
export interface SharedConfig {
  // 配置了 eager 是告訴 webpack 該模塊是作為一個(gè) initial chunk,無(wú)論怎么樣,初始化都需要加載該模塊
	eager?: boolean;
	// 共享模塊依賴(lài)的模塊
	import?: false | SharedItem;
	// 共享模塊的包名
	packageName?: string;
  // 共享模塊的版本 
	requiredVersion?: false | string;
	// 如果配置了 key,查找共享模塊的時(shí)候,會(huì)在當(dāng)前共享作用域查找配置的 key
	shareKey?: string;
	// 共享作用域
	shareScope?: string;
	// 是否需要保持單例
	singleton?: boolean;
	// 是否需要嚴(yán)格校驗(yàn)共享模塊的版本,只有配置了 requiredVersion 配置該選型才有效
	strictVersion?: boolean;
  // 指定提供的模塊的版本,將會(huì)替代低版本的模塊,但是不會(huì)替代版本更好的模塊
	version?: false | string;
}

SharedConfig類(lèi)型我們就可以看到 shared配置有很多的場(chǎng)景需要適配,每個(gè)配置我都做了簡(jiǎn)單注釋來(lái)介紹。當(dāng)然可能這個(gè)時(shí)候,不熟悉 MF 的小伙伴看到這些配置可能是懵逼的狀態(tài)。不用著急,這些配置項(xiàng),在后面更加具體的源碼使用場(chǎng)景,我會(huì)再進(jìn)行介紹,這里先留個(gè)印象。

我們還是看下 app1 的配置:

shared: {
	'react': {
   	 singleton: true, 
     requiredVersion: require('./package.json').dependencies.react
   },
   'react-dom': {
      singleton: true,
      requiredVersion: require('./package.json').dependencies['react-dom']
    },
    'lodash': {
      requiredVersion: require('./package.json').dependencies['lodash'],
      singleton: true,
   }
}

這里分別將 react、react-dom、lodash等三方包配置成了 shared,這樣有什么作用了?

實(shí)際上 shared配置是告訴 Webpack 這些依賴(lài)需要共享(復(fù)用) ,因?yàn)樵?MF 的遠(yuǎn)程模塊消費(fèi)機(jī)制里面,多個(gè)應(yīng)用之間可能會(huì)依賴(lài)相同的三方包,如果沒(méi)有一個(gè)共享機(jī)制,那么一定會(huì)導(dǎo)致多余的 chunk 加載,而且還有其它需要解決的問(wèn)題。

以前面的 app1 和 app2 為例,app1 本身是一個(gè) react 應(yīng)用,它依賴(lài)了 app2 的一個(gè)組件,而 app2 同樣也依賴(lài)了 react ,那么如果沒(méi)有這個(gè)共享模塊的機(jī)制,那么 app1 消費(fèi) app2 的組件可能就還需要加載 app2 的構(gòu)建的 react 依賴(lài)。而且我們知道,react 的運(yùn)行機(jī)制是在同一個(gè)JS runtime 里面,是不能同時(shí)存在兩個(gè) react 實(shí)例的,這也是

singleton配置的由來(lái),它的作用就是為了解決類(lèi)似這樣的場(chǎng)景。

當(dāng)然,要講清楚這部分的原理,還有運(yùn)行機(jī)制,除了需要一定的 MF 使用經(jīng)驗(yàn)外,還需要對(duì)其源碼有一定的了解,我們后續(xù)在剖析 SharedPlugin插件源碼時(shí)再詳細(xì)聊。

小結(jié)

雖然 MF 插件入口的源碼部分相對(duì)來(lái)說(shuō)還是不復(fù)雜的,所以本小節(jié)我們聚焦在其配置上。實(shí)際上對(duì)于 上面提到的一些配置,例如 MF 插件的 library、remoteType等配置在官網(wǎng)是沒(méi)有提到的,包括 exposes、

remotes、shared等配置的一些更加高級(jí)的選項(xiàng),這也是 Webpack 配置復(fù)雜然后官網(wǎng)又不完全介紹一直被人詬病的地方。

總結(jié)

本文我們從 MF 插件主入口出發(fā),分析了其插件的注冊(cè)時(shí)機(jī),并且通過(guò)閱讀這部分的源碼,我們了解到:

  • 插件的配置選項(xiàng)除了常用的 exposesremotes、shared、filename、name 等之外還有

library、remoteType、sharedScope等配置項(xiàng),可以指定 exposesremotes模塊的

library類(lèi)型;

  • MF 核心的源碼實(shí)現(xiàn)是通過(guò)其它三個(gè)插件 ContainerPlugin、

ContainerReferencePluginSharePlugin 等來(lái)實(shí)現(xiàn),然后根據(jù)是否傳入 exposes、

remotes、shared來(lái)決定是否需要初始化各個(gè)插件;

  • exposes、remotes、shared等選項(xiàng)有很多進(jìn)階的配置,特別是 shared配置比較復(fù)雜,從共享三方依賴(lài)、單例、版本鎖定等角度思考,就可以想象這里面的設(shè)計(jì)不簡(jiǎn)單。

后續(xù)文章

下一篇文章,我們開(kāi)始逐漸進(jìn)入深水區(qū),首先深入到 CotainerPlugin的源碼,一步步揭開(kāi)其神秘的面紗。為了更好理解后續(xù)的文章,建議讀者了解一下 Webpack 構(gòu)建流程,特別是核心的構(gòu)建階段,DependencyModule之間的轉(zhuǎn)換流程,更多關(guān)于微前端架構(gòu)ModuleFederationPlugin的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android intent之間復(fù)雜參數(shù)傳遞方法詳解

    Android intent之間復(fù)雜參數(shù)傳遞方法詳解

    這篇文章主要介紹了Android intent之間復(fù)雜參數(shù)傳遞方法,較為詳細(xì)的分析了Android中intent參數(shù)傳遞的常見(jiàn)方法與使用技巧,需要的朋友可以參考下
    2016-10-10
  • 在Android系統(tǒng)源碼中預(yù)置APK的方法

    在Android系統(tǒng)源碼中預(yù)置APK的方法

    今天小編就為大家分享一篇關(guān)于在Android系統(tǒng)源碼中預(yù)置APK的方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • Android Java調(diào)用自己C++類(lèi)庫(kù)的實(shí)例講解

    Android Java調(diào)用自己C++類(lèi)庫(kù)的實(shí)例講解

    今天小編就為大家分享一篇關(guān)于Android Java調(diào)用自己C++類(lèi)庫(kù)的實(shí)例講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02
  • Android改變手機(jī)屏幕朝向的方法

    Android改變手機(jī)屏幕朝向的方法

    這篇文章主要介紹了Android改變手機(jī)屏幕朝向的方法,涉及Android動(dòng)態(tài)更改手機(jī)屏幕朝向的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • 簡(jiǎn)單實(shí)現(xiàn)Android驗(yàn)證碼

    簡(jiǎn)單實(shí)現(xiàn)Android驗(yàn)證碼

    在登錄或者注冊(cè)的時(shí)候要求輸入驗(yàn)證碼,這篇文章主要為大家詳細(xì)介紹了如何簡(jiǎn)單實(shí)現(xiàn)Android驗(yàn)證碼的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Android 防止多次重復(fù)點(diǎn)擊的三種方法的示例

    Android 防止多次重復(fù)點(diǎn)擊的三種方法的示例

    本篇文章主要介紹了Android 防止多次重復(fù)點(diǎn)擊的三種方法的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • Android圖片或拍照選擇圖片功能實(shí)例代碼

    Android圖片或拍照選擇圖片功能實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于Android圖片或拍照選擇圖片功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Android切面編程入門(mén)講解

    Android切面編程入門(mén)講解

    這篇文章給大家分享了關(guān)于Android進(jìn)階資深開(kāi)發(fā)必備技能-切面編程入門(mén)的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們參考下。
    2018-08-08
  • Android 保存Fragment 切換狀態(tài)實(shí)例代碼

    Android 保存Fragment 切換狀態(tài)實(shí)例代碼

    本文主要介紹Android Fragment的應(yīng)用,這里給大家用實(shí)例代碼詳細(xì)介紹了Android Fragment 切換狀態(tài),有需要的小伙伴可以參考下
    2016-07-07
  • Jetpack?Compose?DropdownMenu手指跟隨點(diǎn)擊顯示

    Jetpack?Compose?DropdownMenu手指跟隨點(diǎn)擊顯示

    這篇文章主要為大家介紹了Jetpack?Compose?DropdownMenu手指跟隨點(diǎn)擊位置顯示實(shí)現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11

最新評(píng)論

屏南县| 苍溪县| 丽水市| 忻州市| 贵州省| 麻江县| 深圳市| 宁夏| 富蕴县| 东辽县| 连州市| 平利县| 阳城县| 霍邱县| 西丰县| 常山县| 东安县| 广元市| 前郭尔| 大丰市| 东阳市| 休宁县| 资溪县| 龙川县| 娄底市| 监利县| 辉县市| 遂川县| 搜索| 吴忠市| 宕昌县| 晴隆县| 庄河市| 栾城县| 太白县| 华蓥市| 禹城市| 闸北区| 太保市| 会泽县| 邮箱|