百度工程師帶你瞭解Module Federation
作者 | 一貧
導讀
本文介紹了Module Federation的概念、應用場景,並結合具體的程式碼示例幫助大家對Module Federation的模組共享,公共依賴載入有個初步的認識,方便後續更深入的學習相關內容,同時也給微前端的探索提供一種新的思路,定會給大家一定的提升和啟發。
全文5405字,預計閱讀時間14分鐘。
01 什麼是Module Federation(MF)?
普遍直譯為『模組聯邦』,我們看看官網是怎麼說的?
Motivation
Multiple separate builds should form a single application. These separate builds should not have dependencies between each other, so they can be developed and deployed individually. This is often known as Micro-Frontends, but is not limited to that.
多個獨立的構建可以形成一個應用程式。這些獨立的構建不會相互依賴,因此可以單獨開發和部署它們。
這通常被稱為微前端,但並不僅限於此。
通俗點講,即MF提供了能在當前應用中遠端載入其他伺服器上應用的能力。對此,可以引出下面兩個概念:
- host:引用了其他應用的應用
- remote:被其他應用所使用的應用
△圖片來源於網路
它與我們普遍討論的基座應用、微應用有所不同,它是去中心化的,相互之間是平等的,每個應用是單獨部署在各自的伺服器,每個應用都可以引用其他應用,也能被其他應用所引用,即每個應用可以充當host的角色,亦可以作為remote出現。
△圖片來源於網路
02 應用場景
- 微前端:通過shared以及exposes可以將多個應用引入同一應用中進行管理。
- 資源複用,減少編譯體積:可以將多個應用都用到的通用元件單獨部署,通過MF的功能在runtime時引入到其他專案中,這樣元件程式碼就不會編譯到專案中,同時亦能滿足多個專案同時使用的需求,一舉兩得。
03 如何使用
專案結構如下:
- module-home:首頁,在layout展示一個字串。
- module-layout:佈局,只包含一個html模板。
- module-lib:暴露工具方法,共享lodash庫。
3.1 相關配置引數一覽
3.2 各應用的配置
// apps/module-lib/webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: 'lib',
filename: 'remoteLib.js',
library: { type: 'var', name: 'lib' },
exposes: {
// 提供工具方法
'./utils': './index.js',
},
shared: ["lodash"]
})
]
// apps/module-home/webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: 'home',
filename: 'remoteHome.js',
library: { type: 'var', name: 'home' },
exposes: {
// 提供掛載方法
'./mount': './index.js',
},
shared: ["lodash"]
})
]
// apps/module-layout/webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: 'main',
filename: 'remoteMain.js',
remotes: {
'lib': 'lib@http://localhost:3001/remoteLib.js',
'home': 'home@http://localhost:3003/remoteHome.js',
},
shared: ['lodash']
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './public/index.html'),
inject: 'body'
})
]
// apps/module-layout/boot.js
import {getUid, setUid} from 'lib/utils' // 使用module-lib中暴露的方法
import {mount} from 'home/mount' // 使用module-home中暴露的掛載方法
import _ from 'lodash';
setUid();
setUid();
console.log(getUid())
console.log(_.get)
mount()
如下圖所示:在layout中展示了home掛載的節點,控制檯也列印了呼叫lib中方法的log,同時lib分享的lodash也生效了(全程只加載了一個lodash)。
3.3 以remoteLib為例簡要分析
// 定義全域性變數
var lib;
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "webpack/container/entry/lib":
/*!***********************!*\
!*** container entry ***!
\***********************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
eval("var moduleMap = {\n\t\"./utils\": () => {\n\t\treturn __webpack_require__.e(\"index_js\").then(() => (() => ((__webpack_require__(/*! ./index.js */ \"./index.js\")))));\n\t}\n};\nvar get = (module, getScope) => {\n\t__webpack_require__.R = getScope;\n\tgetScope = (\n\t\t__webpack_require__.o(moduleMap, module)\n\t\t\t? moduleMap[module]()\n\t\t\t: Promise.resolve().then(() => {\n\t\t\t\tthrow new Error('Module \"' + module + '\" does not exist in container.');\n\t\t\t})\n\t);\n\t__webpack_require__.R = undefined;\n\treturn getScope;\n};\nvar init = (shareScope, initScope) => {\n\tif (!__webpack_require__.S) return;\n\tvar name = \"default\"\n\tvar oldScope = __webpack_require__.S[name];\n\tif(oldScope && oldScope !== shareScope) throw new Error(\"Container initialization failed as it has already been initialized with a different share scope\");\n\t__webpack_require__.S[name] = shareScope;\n\treturn __webpack_require__.I(name, initScope);\n};\n\n// This exports getters to disallow modifications\n__webpack_require__.d(exports, {\n\tget: () => (get),\n\tinit: () => (init)\n});\n\n//# sourceURL=webpack://module-lib/container_entry?");
/***/ })
1、moduleMap:用來對映expose的模組
2、get方法:匯出給host應用,用於獲取remote expose的模組
3、init方法:匯出給host應用,用於將remote模組注入
get方法中呼叫了__webpack_require__.e載入chunk
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = (chunkId) => {
/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
/******/ __webpack_require__.f[key](chunkId, promises);
/******/ return promises;
/******/ }, []));
/******/ };
__webpack_require__.e呼叫__webpack_require__.f
/******/ __webpack_require__.f.consumes = (chunkId, promises) => {
/******/ if(__webpack_require__.o(chunkMapping, chunkId)) {
/******/ chunkMapping[chunkId].forEach((id) => {
// 如果host已經有則直接使用,否則去remote安裝
/******/ if(__webpack_require__.o(installedModules, id)) return promises.push(installedModules[id]);
/******/ var onFactory = (factory) => {
/******/ installedModules[id] = 0;
/******/ __webpack_require__.m[id] = (module) => {
/******/ delete __webpack_require__.c[id];
/******/ module.exports = factory();
/******/ }
/******/ };
/******/ var onError = (error) => {
/******/ delete installedModules[id];
/******/ __webpack_require__.m[id] = (module) => {
/******/ delete __webpack_require__.c[id];
/******/ throw error;
/******/ }
/******/ };
/******/ try {
/******/ var promise = moduleToHandlerMapping[id]();
/******/ if(promise.then) {
/******/ promises.push(installedModules[id] = promise.then(onFactory)['catch'](onError));
/******/ } else onFactory(promise);
/******/ } catch(e) { onError(e); }
/******/ });
/******/ }
/******/ }
/******/ })();
- host載入自己的bundle main.js,其中使用jsonp載入對應remote提供的remoteLib.js;
- 在remote中暴露了全域性變數,host將remote注入後可以獲取對應模組,其中的共享模組使用前會檢查自身有沒有,如果沒有再去載入remote的內容。
04 拓展學習
可以學習開源專案:基於 Webpack 5 Module Federation,優雅且實用的微前端解決方案 http://github.com/yuzhanglong/mf-lite。
——END——
參考資料:
[1]How to Build a Micro Frontend with Webpack's Module Federation Plugin
[2]Webpack 新功能 Module Federation 深入解析
[3]基於 Webpack Module Federation,這可能是一個比較優雅的微前端解決方案
[4]探索 webpack5 新特性 Module federation 在騰訊文件的應用
[5]Module Federation原理剖析
推薦閱讀:
- 精準水位在流批一體資料倉庫的探索和實踐
- 視訊編輯場景下的文字模版技術方案
- 淺談活動場景下的圖演算法在反作弊應用
- 百度工程師帶你玩轉正則
- Serverless:基於個性化服務畫像的彈性伸縮實踐
- 百度APP iOS端記憶體優化-原理篇
- 從稀疏表徵出發、召回方向的前沿探索
- 效能平臺數據提速之路
- 採編式AIGC視訊生產流程編排實踐
- 百度工程師漫談視訊理解
- PGLBox 超大規模 GPU 端對端圖學習訓練框架正式釋出
- 百度工程師淺談分散式日誌
- 百度工程師帶你瞭解Module Federation
- 巧用Golang泛型,簡化程式碼編寫
- Go語言DDD實戰初級篇
- 百度工程師帶你玩轉正則
- Diffie-Hellman金鑰協商演算法探究
- 貼吧低程式碼高效能規則引擎設計
- 淺談許可權系統在多利熊業務應用
- 分散式系統關鍵路徑延遲分析實踐