写点什么

百度工程师带你了解 Module Federation

作者:百度Geek说
  • 2023-02-07
    上海
  • 本文字数:3713 字

    阅读完需:约 12 分钟

百度工程师带你了解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.jsplugins: [    new ModuleFederationPlugin({        name: 'lib',        filename: 'remoteLib.js',        library: { type: 'var', name: 'lib' },        exposes: {            // 提供工具方法            './utils': './index.js',        },        shared: ["lodash"]    })]
// apps/module-home/webpack.config.jsplugins: [ new ModuleFederationPlugin({ name: 'home', filename: 'remoteHome.js', library: { type: 'var', name: 'home' }, exposes: { // 提供挂载方法 './mount': './index.js', }, shared: ["lodash"] })]
// apps/module-layout/webpack.config.jsplugins: [ 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.jsimport {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,优雅且实用的微前端解决方案 https://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 原理剖析


推荐阅读


巧用Golang泛型,简化代码编写


Go语言DDD实战初级篇


Diffie-Hellman密钥协商算法探究


贴吧低代码高性能规则引擎设计


浅谈权限系统在多利熊业务应用


分布式系统关键路径延迟分析实践

发布于: 刚刚阅读数: 3
用户头像

百度Geek说

关注

百度官方技术账号 2021-01-22 加入

关注我们,带你了解更多百度技术干货。

评论

发布
暂无评论
百度工程师带你了解Module Federation_JavaScript_百度Geek说_InfoQ写作社区