模块联邦完成

This commit is contained in:
YuanJin 2022-02-08 17:58:00 +08:00
parent dad49e2e90
commit 9ffd5e9aa1
76 changed files with 93194 additions and 15 deletions

View File

@ -0,0 +1,64 @@
!(function (e) {
var t = {};
function r(n) {
if (t[n]) return t[n].exports;
var o = (t[n] = { i: n, l: !1, exports: {} });
return e[n].call(o.exports, o, o.exports, r), (o.l = !0), o.exports;
}
(r.m = e),
(r.c = t),
(r.d = function (e, t, n) {
r.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: n });
}),
(r.r = function (e) {
'undefined' != typeof Symbol &&
Symbol.toStringTag &&
Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }),
Object.defineProperty(e, '__esModule', { value: !0 });
}),
(r.t = function (e, t) {
if ((1 & t && (e = r(e)), 8 & t)) return e;
if (4 & t && 'object' == typeof e && e && e.__esModule) return e;
var n = Object.create(null);
if (
(r.r(n),
Object.defineProperty(n, 'default', { enumerable: !0, value: e }),
2 & t && 'string' != typeof e)
)
for (var o in e)
r.d(
n,
o,
function (t) {
return e[t];
}.bind(null, o)
);
return n;
}),
(r.n = function (e) {
var t =
e && e.__esModule
? function () {
return e.default;
}
: function () {
return e;
};
return r.d(t, 'a', t), t;
}),
(r.o = function (e, t) {
return Object.prototype.hasOwnProperty.call(e, t);
}),
(r.p = ''),
r((r.s = 0));
})([
function (e, t, r) {
'use strict';
function n(e, t) {
const r = Math.random() * (t - e);
return Math.floor(r) + e;
}
r.r(t);
for (let e = 1; e < 20; e++) console.log(n(1, 10));
},
]);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --mode production",
"dev": "webpack --mode development"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.46.0",
"webpack-cli": "^4.9.1"
}
}

View File

@ -0,0 +1,5 @@
import { getRandom } from './module';
for (let i = 1; i < 20; i++) {
console.log(getRandom(1, 10));
}

View File

@ -0,0 +1,4 @@
export function getRandom(min, max) {
const n = Math.random() * (max - min);
return Math.floor(n) + min;
}

View File

@ -0,0 +1,3 @@
module.exports = {
devtool: 'none',
};

View File

@ -0,0 +1,19 @@
# webpack scope hoisting
详细介绍https://webpack.docschina.org/plugins/module-concatenation-plugin/
## 面试题
介绍一下 webpack scope hoisting
> 参考答案:
>
> scope hoisting 是 webpack 的内置优化,它是针对模块的优化,在生产环境打包时会自动开启。
>
> 在未开启scope hoisting时webpack 会将每个模块的代码放置在一个独立的函数环境中,这样是为了保证模块的作用域互不干扰。
>
> 而 scope hoisting 的作用恰恰相反是把多个模块的代码合并到一个函数环境中执行。在这一过程中webpack 会按照顺序正确的合并模块代码,同时对涉及的标识符做适当处理以避免重名。
>
> 这样做的好处是减少了函数调用,对运行效率有一定提升,同时也降低了打包体积。
>
> 但 scope hoisting 的启用是有前提的,如果遇到某些模块多次被其他模块引用,或者使用了动态导入的模块,或者是非 ESM 的模块,都不会有 scope hoisting。

View File

@ -0,0 +1,222 @@
/******/ (() => {
// webpackBootstrap
/******/ 'use strict';
/******/ var __webpack_modules__ = {
/***/ './src/index.js':
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/***/ (module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.a(
module,
async (__webpack_handle_async_dependencies__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
/* harmony export */
});
const resp = await fetch('http://www.baidu.com');
const jsonBody = await resp.json();
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ =
jsonBody;
__webpack_handle_async_dependencies__();
},
1
);
/***/
},
/******/
};
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/
}
/******/ // Create a new module (and put it into the cache)
/******/ var module = (__webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {},
/******/
});
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](
module,
module.exports,
__webpack_require__
);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/
}
/******/
/************************************************************************/
/******/ /* webpack/runtime/async module */
/******/ (() => {
/******/ var webpackThen =
typeof Symbol === 'function'
? Symbol('webpack then')
: '__webpack_then__';
/******/ var webpackExports =
typeof Symbol === 'function'
? Symbol('webpack exports')
: '__webpack_exports__';
/******/ var completeQueue = (queue) => {
/******/ if (queue) {
/******/ queue.forEach((fn) => fn.r--);
/******/ queue.forEach((fn) => (fn.r-- ? fn.r++ : fn()));
/******/
}
/******/
};
/******/ var completeFunction = (fn) => !--fn.r && fn();
/******/ var queueFunction = (queue, fn) =>
queue ? queue.push(fn) : completeFunction(fn);
/******/ var wrapDeps = (deps) =>
deps.map((dep) => {
/******/ if (dep !== null && typeof dep === 'object') {
/******/ if (dep[webpackThen]) return dep;
/******/ if (dep.then) {
/******/ var queue = [];
/******/ dep.then((r) => {
/******/ obj[webpackExports] = r;
/******/ completeQueue(queue);
/******/ queue = 0;
/******/
});
/******/ var obj = {};
/******/ obj[webpackThen] = (fn, reject) => (
queueFunction(queue, fn), dep['catch'](reject)
);
/******/ return obj;
/******/
}
/******/
}
/******/ var ret = {};
/******/ ret[webpackThen] = (fn) => completeFunction(fn);
/******/ ret[webpackExports] = dep;
/******/ return ret;
/******/
});
/******/ __webpack_require__.a = (module, body, hasAwait) => {
/******/ var queue = hasAwait && [];
/******/ var exports = module.exports;
/******/ var currentDeps;
/******/ var outerResolve;
/******/ var reject;
/******/ var isEvaluating = true;
/******/ var nested = false;
/******/ var whenAll = (deps, onResolve, onReject) => {
/******/ if (nested) return;
/******/ nested = true;
/******/ onResolve.r += deps.length;
/******/ deps.map((dep, i) => dep[webpackThen](onResolve, onReject));
/******/ nested = false;
/******/
};
/******/ var promise = new Promise((resolve, rej) => {
/******/ reject = rej;
/******/ outerResolve = () => (
resolve(exports), completeQueue(queue), (queue = 0)
);
/******/
});
/******/ promise[webpackExports] = exports;
/******/ promise[webpackThen] = (fn, rejectFn) => {
/******/ if (isEvaluating) {
return completeFunction(fn);
}
/******/ if (currentDeps) whenAll(currentDeps, fn, rejectFn);
/******/ queueFunction(queue, fn);
/******/ promise['catch'](rejectFn);
/******/
};
/******/ module.exports = promise;
/******/ body((deps) => {
/******/ if (!deps) return outerResolve();
/******/ currentDeps = wrapDeps(deps);
/******/ var fn, result;
/******/ var promise = new Promise((resolve, reject) => {
/******/ fn = () =>
resolve((result = currentDeps.map((d) => d[webpackExports])));
/******/ fn.r = 0;
/******/ whenAll(currentDeps, fn, reject);
/******/
});
/******/ return fn.r ? promise : result;
/******/
}).then(outerResolve, reject);
/******/ isEvaluating = false;
/******/
};
/******/
})();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for (var key in definition) {
/******/ if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
/******/ Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
});
/******/
}
/******/
}
/******/
};
/******/
})();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
/******/
})();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, {
value: 'Module',
});
/******/
}
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/
};
/******/
})();
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module used 'module' so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__('./src/index.js');
/******/
/******/
})();
//# sourceMappingURL=main.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
{
"name": "top-level-await",
"version": "1.0.0",
"description": "",
"main": "index.js",
"devDependencies": {
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"
},
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,3 @@
const resp = await fetch("http://www.baidu.com");
const jsonBody = await resp.json();
export default jsonBody;

View File

@ -0,0 +1,8 @@
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/index.js',
experiments: {
topLevelAwait: true,
},
};

View File

@ -0,0 +1,2 @@
console.log("index1.js没有依赖其他模块也没有导出任何东西");
//# sourceMappingURL=index1.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index1.js","mappings":"AAAAA,QAAQC,IAAI","sources":["webpack://op-package/./src/index1.js"],"sourcesContent":["console.log(\"index1.js没有依赖其他模块也没有导出任何东西\");\n"],"names":["console","log"],"sourceRoot":""}

View File

@ -0,0 +1,5 @@
(() => {
'use strict';
console.log('f1'), console.log('f4');
})();
//# sourceMappingURL=index2.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"index2.js","mappings":"mBAEEA,QAAQC,IAAI,MCGZD,QAAQC,IAAI,O","sources":["webpack://op-package/./src/modules/a.js","webpack://op-package/./src/modules/b.js"],"sourcesContent":["import { f3 } from './b';\nexport function f1() {\n console.log('f1');\n}\n\nexport function f2() {\n console.log('f2');\n f3();\n}\n","export function f3() {\n console.log(\"f3\");\n}\n\nexport function f4() {\n console.log(\"f4\");\n}\n"],"names":["console","log"],"sourceRoot":""}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
{
"name": "op-package",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"
}
}

View File

@ -0,0 +1 @@
console.log("index1.js没有依赖其他模块也没有导出任何东西");

View File

@ -0,0 +1,4 @@
import { f1 } from './modules/a';
import * as bFuncs from './modules/b';
f1();
bFuncs.f4();

View File

@ -0,0 +1,9 @@
import { f3 } from './b';
export function f1() {
console.log('f1');
}
export function f2() {
console.log('f2');
f3();
}

View File

@ -0,0 +1,7 @@
export function f3() {
console.log("f3");
}
export function f4() {
console.log("f4");
}

View File

@ -0,0 +1,8 @@
module.exports = {
mode: "production",
devtool: "source-map",
entry: {
index1: "./src/index1.js",
index2: "./src/index2.js",
},
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
{
"name": "package-cache",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"lodash": "^4.17.20",
"moment": "^2.29.1"
}
}

View File

@ -0,0 +1,3 @@
import _ from "lodash";
import moment from "moment";
console.log(_, moment);

View File

@ -0,0 +1,12 @@
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/index.js',
cache: {
type: 'filesystem', // 缓存类型支持memory、filesystem
cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'), // 缓存目录,仅类型为 filesystem 有效
// 更多配置参考https://webpack.docschina.org/configuration/other-options/#cache
},
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"
}
}

View File

@ -0,0 +1,6 @@
module.exports = {
mode: 'production',
output: {
clean: true,
},
};

View File

@ -0,0 +1,68 @@
# 清除输出目录
`webpack5`清除输出目录开箱可用,无须安装`clean-webpack-plugin`,具体做法如下:
```js
module.exports = {
output: {
clean: true
}
}
```
# top-level-await
`webpack5`现在允许在模块的顶级代码中直接使用`await`
```js
// src/index.js
const resp = await fetch("http://www.baidu.com");
const jsonBody = await resp.json();
export default jsonBody;
```
目前,`top-level-await`还未成为正式标准,因此,对于`webpack5`而言,该功能是作为`experiments`发布的,需要在`webpack.config.js`中配置开启
```js
// webpack.config.js
module.exports = {
experiments: {
topLevelAwait: true,
},
};
```
# 打包体积优化
`webpack5`对模块的合并、作用域提升、`tree shaking`等处理更加智能
# 打包缓存开箱即用
`webpack4`中,需要使用`cache-loader`缓存打包结果以优化之后的打包性能
而在`webpack5`中,默认就已经开启了打包缓存,无须再安装`cache-loader`
默认情况下,`webpack5`是将模块的打包结果缓存到内存中,可以通过`cache`配置进行更改
```js
const path = require("path");
module.exports = {
cache: {
// 缓存类型支持memory、filesystem
type: "filesystem",
// 缓存目录,仅类型为 filesystem 有效
cacheDirectory: path.resolve(__dirname, "node_modules/.cache/webpack"),
},
};
```
> 关于`cache`的更多配置参考https://webpack.docschina.org/configuration/other-options/#cache
# 资源模块
`webpack4`中,针对资源型文件我们通常使用`file-loader``url-loader``raw-loader`进行处理
由于大部分前端项目都会用到资源型文件,因此`webpack5`原生支持了资源型模块
详见https://webpack.docschina.org/guides/asset-modules/

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="main.js"></script></head>
<body>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"main.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA,GAAG;WACH;WACA;WACA,CAAC;;;;;WCPD;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;WCNA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;;;;;;;;;;;;;ACf0C,CAAC;AACG,CAAC;AACE,CAAC;AACf,CAAC;;AAEpC,2BAA2B,gDAAM;AACjC,6BAA6B,kDAAQ;AACrC,8BAA8B,mDAAU;AACxC,uBAAuB,4CAAG","sources":["webpack://resource-module/webpack/bootstrap","webpack://resource-module/webpack/runtime/global","webpack://resource-module/webpack/runtime/make namespace object","webpack://resource-module/webpack/runtime/publicPath","webpack://resource-module/./src/index.js"],"sourcesContent":["// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + \"\";\nvar document = __webpack_require__.g.document;\nif (!scriptUrl && document) {\n\tif (document.currentScript)\n\t\tscriptUrl = document.currentScript.src\n\tif (!scriptUrl) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tif(scripts.length) scriptUrl = scripts[scripts.length - 1].src\n\t}\n}\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl;","import bigPic from './assets/big-pic.png'; // 期望得到路径\nimport smallPic from './assets/small-pic.jpg'; // 期望得到base64\nimport yueyunpeng from './assets/yueyunpeng.gif'; // 期望根据文件大小决定是路径还是base64\nimport raw from './assets/raw.txt'; // 期望得到原始文件内容\n\nconsole.log('big-pic.png', bigPic);\nconsole.log('small-pic.jpg', smallPic);\nconsole.log('yueyunpeng.gif', yueyunpeng);\nconsole.log('raw.txt', raw);\n"],"names":[],"sourceRoot":""}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
{
"name": "resource-module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack serve"
},
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^5.0.0-beta.6",
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^3.11.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@ -0,0 +1 @@
本质上webpack 是一个用于现代 JavaScript 应用程序的_静态模块打包工具_。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

View File

@ -0,0 +1,9 @@
import bigPic from './assets/big-pic.png'; // 期望得到路径
import smallPic from './assets/small-pic.jpg'; // 期望得到base64
import yueyunpeng from './assets/yueyunpeng.gif'; // 期望根据文件大小决定是路径还是base64
import raw from './assets/raw.txt'; // 期望得到原始文件内容
console.log('big-pic.png', bigPic);
console.log('small-pic.jpg', smallPic);
console.log('yueyunpeng.gif', yueyunpeng);
console.log('raw.txt', raw);

View File

@ -0,0 +1,44 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/index.js',
devServer: {
port: 8080,
},
plugins: [new HtmlWebpackPlugin()],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'assets/[hash:5][ext]', // 在这里自定义资源文件保存的文件名
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource', // 作用类似于 file-loader
},
{
test: /\.jpg/,
type: 'asset/inline', // 作用类似于 url-loader 文件大小不足的场景
},
{
test: /\.txt/,
type: 'asset/source', // 作用类似于 raw-loader
},
{
test: /\.gif/,
type: 'asset', // 作用类似于 url-loader。在导出一个 data uri 和发送一个单独的文件之间自动选择
generator: {
filename: 'gif/[hash:5][ext]', // 这里的配置会覆盖 assetModuleFilename
},
parser: {
dataUrlCondition: {
maxSize: 4 * 1024, // 4kb以下使用 data uri
},
},
},
],
},
};

View File

@ -0,0 +1,22 @@
解释一下 npm 模块安装机制是什么?
> 参考答案:
>
> 1. npm 会检查本地的 node_modules 目录中是否已经安装过该模块,如果已经安装,则不再重新安装
> 2. npm 检查缓存中是否有相同的模块,如果有,直接从缓存中读取安装
> 3. 如果本地和缓存中均不存在npm 会从 registry 指定的地址下载安装包,然后将其写入到本地的 node_modules 目录中,同时缓存起来。
> npm 缓存相关命令:
>
> ```shell
> # 清除缓存
> npm cache clean -f
>
> # 获取缓存位置
> npm config get cache
>
> # 设置缓存位置
> npm config set cache "新的缓存路径"
> ```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
{
"name": "active",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack serve"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
},
"dependencies": {
"jquery": "^3.6.0"
}
}

View File

@ -0,0 +1,12 @@
import $ from 'jquery';
import news from './news';
import now from 'home/now';
// 生成活动页标题
$('<h1>').text('活动页').appendTo(document.body);
// 远程引入时间模块
now($('<div>').appendTo(document.body));
// 活动页中有一个新闻列表
news($('<div>').appendTo(document.body));

View File

@ -0,0 +1 @@
import('./bootstrap');

View File

@ -0,0 +1,10 @@
import $ from 'jquery';
export default function (container) {
const ul = $('<ul>').appendTo(container);
let html = '';
for (var i = 1; i <= 20; i++) {
html += `<li>新闻${i}</li>`;
}
ul.html(html);
}

View File

@ -0,0 +1,44 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index.js',
mode: 'development',
devtool: 'source-map',
devServer: {
port: 3000,
},
output: {
clean: true,
},
plugins: [
new HtmlWebpackPlugin(),
new ModuleFederationPlugin({
// 远程使用其他项目暴露的模块
remotes: {
// key: 自定义远程暴露的联邦名
// 比如为 abc 则之后引用该联邦的模块则使用 import "abc/模块名"
// value: 模块联邦名@模块联邦访问地址
// 远程访问时,将从下面的地址加载
home: 'home@http://localhost:8080/home-entry.js',
},
// 模块联邦的名称
// 该名称将成为一个全部变量,通过该变量将可获取当前联邦的所有暴露模块
name: 'active',
// 模块联邦生成的文件名,全部变量将置入到该文件中
filename: 'active-entry.js',
// 模块联邦暴露的所有模块
exposes: {
// key相对于模块联邦的路径
// 这里的 ./news 将决定该模块的访问路径为 active/news
// value: 模块的具体路径
'./news': './src/news.js',
},
shared: {
// jquery为共享模块
jquery: {
singleton: true, // 全局唯一
},
},
}),
],
};

View File

@ -0,0 +1,308 @@
var home;
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({});
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // 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/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/global */
/******/ (() => {
/******/ __webpack_require__.g = (function() {
/******/ if (typeof globalThis === 'object') return globalThis;
/******/ try {
/******/ return this || new Function('return this')();
/******/ } catch (e) {
/******/ if (typeof window === 'object') return window;
/******/ }
/******/ })();
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/load script */
/******/ (() => {
/******/ var inProgress = {};
/******/ var dataWebpackPrefix = "home:";
/******/ // loadScript function to load a script via script tag
/******/ __webpack_require__.l = (url, done, key, chunkId) => {
/******/ if(inProgress[url]) { inProgress[url].push(done); return; }
/******/ var script, needAttach;
/******/ if(key !== undefined) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ for(var i = 0; i < scripts.length; i++) {
/******/ var s = scripts[i];
/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
/******/ }
/******/ }
/******/ if(!script) {
/******/ needAttach = true;
/******/ script = document.createElement('script');
/******/
/******/ script.charset = 'utf-8';
/******/ script.timeout = 120;
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key);
/******/ script.src = url;
/******/ }
/******/ inProgress[url] = [done];
/******/ var onScriptComplete = (prev, event) => {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var doneFns = inProgress[url];
/******/ delete inProgress[url];
/******/ script.parentNode && script.parentNode.removeChild(script);
/******/ doneFns && doneFns.forEach((fn) => (fn(event)));
/******/ if(prev) return prev(event);
/******/ }
/******/ ;
/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
/******/ script.onerror = onScriptComplete.bind(null, script.onerror);
/******/ script.onload = onScriptComplete.bind(null, script.onload);
/******/ needAttach && document.head.appendChild(script);
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
/******/ var scriptUrl;
/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
/******/ var document = __webpack_require__.g.document;
/******/ if (!scriptUrl && document) {
/******/ if (document.currentScript)
/******/ scriptUrl = document.currentScript.src
/******/ if (!scriptUrl) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
/******/ }
/******/ }
/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
/******/ __webpack_require__.p = scriptUrl;
/******/ })();
/******/
/******/ /* webpack/runtime/jsonp chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "home": 0
/******/ };
/******/
/******/ __webpack_require__.f.j = (chunkId, promises) => {
/******/ // JSONP chunk loading for javascript
/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ if(true) { // all chunks have JS
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId);
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ var loadingEnded = (event) => {
/******/ if(__webpack_require__.o(installedChunks, chunkId)) {
/******/ installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/ if(installedChunkData) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ installedChunkData[1](error);
/******/ }
/******/ }
/******/ };
/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId);
/******/ } else installedChunks[chunkId] = 0;
/******/ }
/******/ }
/******/ };
/******/
/******/ // no prefetching
/******/
/******/ // no preloaded
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/
/******/ // no on chunks loaded
/******/
/******/ // install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0;
/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) var result = runtime(__webpack_require__);
/******/ }
/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ installedChunks[chunkId][0]();
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/
/******/ }
/******/
/******/ var chunkLoadingGlobal = self["webpackChunkhome"] = self["webpackChunkhome"] || [];
/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
(() => {
var exports = __webpack_exports__;
/*!***********************!*\
!*** container entry ***!
\***********************/
var moduleMap = {
"./now": () => {
return Promise.all([__webpack_require__.e("vendors-node_modules_jquery_dist_jquery_js"), __webpack_require__.e("src_now_js")]).then(() => (() => ((__webpack_require__(/*! ./src/now.js */ "./src/now.js")))));
}
};
var get = (module, getScope) => {
__webpack_require__.R = getScope;
getScope = (
__webpack_require__.o(moduleMap, module)
? moduleMap[module]()
: Promise.resolve().then(() => {
throw new Error('Module "' + module + '" does not exist in container.');
})
);
__webpack_require__.R = undefined;
return getScope;
};
var init = (shareScope, initScope) => {
if (!__webpack_require__.S) return;
var name = "default"
var oldScope = __webpack_require__.S[name];
if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");
__webpack_require__.S[name] = shareScope;
return __webpack_require__.I(name, initScope);
};
// This exports getters to disallow modifications
__webpack_require__.d(exports, {
get: () => (get),
init: () => (init)
});
})();
home = __webpack_exports__;
/******/ })()
;
//# sourceMappingURL=home-entry.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="main.js"></script><script defer src="home-entry.js"></script></head>
<body>
</body>
</html>

271
08. 模块联邦/demo/home/dist/main.js vendored Normal file
View File

@ -0,0 +1,271 @@
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({});
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = __webpack_modules__;
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/ensure chunk */
/******/ (() => {
/******/ __webpack_require__.f = {};
/******/ // 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/runtime/get javascript chunk filename */
/******/ (() => {
/******/ // This function allow to reference async chunks
/******/ __webpack_require__.u = (chunkId) => {
/******/ // return url for filenames based on template
/******/ return "" + chunkId + ".js";
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/global */
/******/ (() => {
/******/ __webpack_require__.g = (function() {
/******/ if (typeof globalThis === 'object') return globalThis;
/******/ try {
/******/ return this || new Function('return this')();
/******/ } catch (e) {
/******/ if (typeof window === 'object') return window;
/******/ }
/******/ })();
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/load script */
/******/ (() => {
/******/ var inProgress = {};
/******/ var dataWebpackPrefix = "home:";
/******/ // loadScript function to load a script via script tag
/******/ __webpack_require__.l = (url, done, key, chunkId) => {
/******/ if(inProgress[url]) { inProgress[url].push(done); return; }
/******/ var script, needAttach;
/******/ if(key !== undefined) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ for(var i = 0; i < scripts.length; i++) {
/******/ var s = scripts[i];
/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
/******/ }
/******/ }
/******/ if(!script) {
/******/ needAttach = true;
/******/ script = document.createElement('script');
/******/
/******/ script.charset = 'utf-8';
/******/ script.timeout = 120;
/******/ if (__webpack_require__.nc) {
/******/ script.setAttribute("nonce", __webpack_require__.nc);
/******/ }
/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key);
/******/ script.src = url;
/******/ }
/******/ inProgress[url] = [done];
/******/ var onScriptComplete = (prev, event) => {
/******/ // avoid mem leaks in IE.
/******/ script.onerror = script.onload = null;
/******/ clearTimeout(timeout);
/******/ var doneFns = inProgress[url];
/******/ delete inProgress[url];
/******/ script.parentNode && script.parentNode.removeChild(script);
/******/ doneFns && doneFns.forEach((fn) => (fn(event)));
/******/ if(prev) return prev(event);
/******/ }
/******/ ;
/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
/******/ script.onerror = onScriptComplete.bind(null, script.onerror);
/******/ script.onload = onScriptComplete.bind(null, script.onload);
/******/ needAttach && document.head.appendChild(script);
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/publicPath */
/******/ (() => {
/******/ var scriptUrl;
/******/ if (__webpack_require__.g.importScripts) scriptUrl = __webpack_require__.g.location + "";
/******/ var document = __webpack_require__.g.document;
/******/ if (!scriptUrl && document) {
/******/ if (document.currentScript)
/******/ scriptUrl = document.currentScript.src
/******/ if (!scriptUrl) {
/******/ var scripts = document.getElementsByTagName("script");
/******/ if(scripts.length) scriptUrl = scripts[scripts.length - 1].src
/******/ }
/******/ }
/******/ // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
/******/ // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
/******/ if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
/******/ scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
/******/ __webpack_require__.p = scriptUrl;
/******/ })();
/******/
/******/ /* webpack/runtime/jsonp chunk loading */
/******/ (() => {
/******/ // no baseURI
/******/
/******/ // object to store loaded and loading chunks
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
/******/ var installedChunks = {
/******/ "main": 0
/******/ };
/******/
/******/ __webpack_require__.f.j = (chunkId, promises) => {
/******/ // JSONP chunk loading for javascript
/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
/******/ if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ promises.push(installedChunkData[2]);
/******/ } else {
/******/ if(true) { // all chunks have JS
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
/******/ promises.push(installedChunkData[2] = promise);
/******/
/******/ // start chunk loading
/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId);
/******/ // create error before stack unwound to get useful stacktrace later
/******/ var error = new Error();
/******/ var loadingEnded = (event) => {
/******/ if(__webpack_require__.o(installedChunks, chunkId)) {
/******/ installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined;
/******/ if(installedChunkData) {
/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ var realSrc = event && event.target && event.target.src;
/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ error.name = 'ChunkLoadError';
/******/ error.type = errorType;
/******/ error.request = realSrc;
/******/ installedChunkData[1](error);
/******/ }
/******/ }
/******/ };
/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId);
/******/ } else installedChunks[chunkId] = 0;
/******/ }
/******/ }
/******/ };
/******/
/******/ // no prefetching
/******/
/******/ // no preloaded
/******/
/******/ // no HMR
/******/
/******/ // no HMR manifest
/******/
/******/ // no on chunks loaded
/******/
/******/ // install a JSONP callback for chunk loading
/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
/******/ var [chunkIds, moreModules, runtime] = data;
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0;
/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) {
/******/ for(moduleId in moreModules) {
/******/ if(__webpack_require__.o(moreModules, moduleId)) {
/******/ __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(runtime) var result = runtime(__webpack_require__);
/******/ }
/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ installedChunks[chunkId][0]();
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/
/******/ }
/******/
/******/ var chunkLoadingGlobal = self["webpackChunkhome"] = self["webpackChunkhome"] || [];
/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
Promise.all(/*! import() */[__webpack_require__.e("vendors-node_modules_jquery_dist_jquery_js"), __webpack_require__.e("src_bootstrap_js")]).then(__webpack_require__.bind(__webpack_require__, /*! ./bootstrap */ "./src/bootstrap.js"));
/******/ })()
;
//# sourceMappingURL=main.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,51 @@
"use strict";
(self["webpackChunkhome"] = self["webpackChunkhome"] || []).push([["src_bootstrap_js"],{
/***/ "./src/bootstrap.js":
/*!**************************!*\
!*** ./src/bootstrap.js ***!
\**************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js");
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _now__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./now */ "./src/now.js");
// 生成首页标题
jquery__WEBPACK_IMPORTED_MODULE_0___default()('<h1>').text('首页').appendTo(document.body);
// 首页中有一个显示当前时间的区域
(0,_now__WEBPACK_IMPORTED_MODULE_1__["default"])(jquery__WEBPACK_IMPORTED_MODULE_0___default()('<div>').appendTo(document.body));
/***/ }),
/***/ "./src/now.js":
/*!********************!*\
!*** ./src/now.js ***!
\********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js");
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(container) {
const p = jquery__WEBPACK_IMPORTED_MODULE_0___default()('<p>').appendTo(container).text(new Date().toLocaleString());
setInterval(function () {
p.text(new Date().toLocaleString());
}, 1000);
}
/***/ })
}]);
//# sourceMappingURL=src_bootstrap_js.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"src_bootstrap_js.js","mappings":";;;;;;;;;;;;;AAAuB;AACC;;AAExB;AACA,6CAAC;;AAED;AACA,gDAAG,CAAC,6CAAC;;;;;;;;;;;;;;;;;ACPkB;;AAEvB,6BAAe,oCAAU;AACzB,YAAY,6CAAC;AACb;AACA;AACA,GAAG;AACH","sources":["webpack://home/./src/bootstrap.js","webpack://home/./src/now.js"],"sourcesContent":["import $ from 'jquery';\nimport now from './now';\n\n// 生成首页标题\n$('<h1>').text('首页').appendTo(document.body);\n\n// 首页中有一个显示当前时间的区域\nnow($('<div>').appendTo(document.body));\n","import $ from 'jquery';\n\nexport default function (container) {\n const p = $('<p>').appendTo(container).text(new Date().toLocaleString());\n setInterval(function () {\n p.text(new Date().toLocaleString());\n }, 1000);\n}\n"],"names":[],"sourceRoot":""}

View File

@ -0,0 +1,29 @@
"use strict";
(self["webpackChunkhome"] = self["webpackChunkhome"] || []).push([["src_now_js"],{
/***/ "./src/now.js":
/*!********************!*\
!*** ./src/now.js ***!
\********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* export default binding */ __WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "./node_modules/jquery/dist/jquery.js");
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
/* harmony default export */ function __WEBPACK_DEFAULT_EXPORT__(container) {
const p = jquery__WEBPACK_IMPORTED_MODULE_0___default()('<p>').appendTo(container).text(new Date().toLocaleString());
setInterval(function () {
p.text(new Date().toLocaleString());
}, 1000);
}
/***/ })
}]);
//# sourceMappingURL=src_now_js.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"src_now_js.js","mappings":";;;;;;;;;;;;;;;AAAuB;;AAEvB,6BAAe,oCAAU;AACzB,YAAY,6CAAC;AACb;AACA;AACA,GAAG;AACH","sources":["webpack://home/./src/now.js"],"sourcesContent":["import $ from 'jquery';\n\nexport default function (container) {\n const p = $('<p>').appendTo(container).text(new Date().toLocaleString());\n setInterval(function () {\n p.text(new Date().toLocaleString());\n }, 1000);\n}\n"],"names":[],"sourceRoot":""}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
{
"name": "home",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack serve"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^5.5.0",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
},
"dependencies": {
"jquery": "^3.6.0"
}
}

View File

@ -0,0 +1,12 @@
import $ from 'jquery';
import now from './now';
import news from 'active/news';
// 生成首页标题
$('<h1>').text('首页').appendTo(document.body);
// 首页中有一个显示当前时间的区域
now($('<div>').appendTo(document.body));
// 新闻列表
news($('<div>').appendTo(document.body));

View File

@ -0,0 +1 @@
import('./bootstrap');

View File

@ -0,0 +1,8 @@
import $ from 'jquery';
export default function (container) {
const p = $('<p>').appendTo(container).text(new Date().toLocaleString());
setInterval(function () {
p.text(new Date().toLocaleString());
}, 1000);
}

View File

@ -0,0 +1,43 @@
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
entry: './src/index.js',
mode: 'development',
devtool: 'source-map',
devServer: {
port: 8080,
},
output: {
clean: true,
},
plugins: [
new HtmlWebpackPlugin(),
new ModuleFederationPlugin({
// 模块联邦的名称
// 该名称将成为一个全部变量,通过该变量将可获取当前联邦的所有暴露模块
name: 'home',
// 模块联邦生成的文件名,全部变量将置入到该文件中
filename: 'home-entry.js',
// 模块联邦暴露的所有模块
exposes: {
// key相对于模块联邦的路径
// 这里的 ./now 将决定该模块的访问路径为 home/now
// value: 模块的具体路径
'./now': './src/now.js',
},
remotes: {
// key: 自定义远程暴露的联邦名
// 比如为 abc 则之后引用该联邦的模块则使用 import "abc/模块名"
// value: 模块联邦名@模块联邦访问地址
// 远程访问时,将从下面的地址加载
active: 'active@http://localhost:3000/active-entry.js',
},
shared: {
// jquery为共享模块
jquery: {
singleton: true, // 全局唯一
},
},
}),
],
};

296
08. 模块联邦/课件.md Normal file
View File

@ -0,0 +1,296 @@
# 模块联邦
在大型项目中,往往会把项目中的某个区域或功能模块作为单独的项目开发,最终形成「微前端」架构
在微前端架构中,不同的工程可能出现下面的场景
![image-20210122172549530](http://mdrs.yuanjin.tech/img/20210122172549.png)
这涉及到很多非常棘手的问题:
- 如何避免公共模块重复打包
- 如何将某个项目中一部分模块分享出去,同时还要避免重复打包
- 如何管理依赖的不同版本
- 如何更新模块
- `......`
`webpack5`尝试着通过`模块联邦`来解决此类问题
## 示例
现有两个微前端工程,它们各自独立开发、测试、部署,但它们有一些相同的公共模块,并有一些自己的模块需要分享给其他工程使用,同时又要引入其他工程的模块。
<img src="http://mdrs.yuanjin.tech/img/20220208150521.png" alt="image-20220208150515731" style="zoom:50%;" />
### 初始化工程
#### home项目
安装
```shell
# 初始化 package.json
npm init -y
# 安装依赖
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin
npm i jquery
```
修改`package.json`
```json
"scripts": {
"build": "webpack",
"dev": "webpack serve"
}
```
配置`webpack.config.js`
```js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
mode: 'development',
devtool: 'source-map',
devServer: {
port: 8080,
},
output: {
clean: true,
},
plugins: [ new HtmlWebpackPlugin() ]
};
```
代码
```js
// src/now.js
import $ from 'jquery';
export default function (container) {
const p = $('<p>').appendTo(container).text(new Date().toLocaleString());
setInterval(function () {
p.text(new Date().toLocaleString());
}, 1000);
}
// src/bootstrap.js
import $ from 'jquery';
import now from './now';
// 生成首页标题
$('<h1>').text('首页').appendTo(document.body);
// 首页中有一个显示当前时间的区域
now($('<div>').appendTo(document.body));
// src/index.js
import('./bootstrap')
```
#### active项目
安装
```shell
# 初始化 package.json
npm init -y
# 安装依赖
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin
npm i jquery
```
修改`package.json`
```json
"scripts": {
"build": "webpack",
"dev": "webpack serve"
}
```
配置`webpack.config.js`
```js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
mode: 'development',
devtool: 'source-map',
devServer: {
port: 3000,
},
output: {
clean: true,
},
plugins: [ new HtmlWebpackPlugin() ]
};
```
代码
```js
// src/news.js
import $ from 'jquery';
export default function (container) {
const ul = $('<ul>').appendTo(container);
let html = '';
for (var i = 1; i <= 20; i++) {
html += `<li>新闻${i}</li>`;
}
ul.html(html);
}
// src/bootstrap.js
import $ from 'jquery';
import news from './news';
// 生成活动页标题
$('<h1>').text('活动页').appendTo(document.body);
// 活动页中有一个新闻列表
news($('<div>').appendTo(document.body));
// src/index.js
import('./bootstrap')
```
### 暴露和引用模块
#### active项目需要使用home项目的now模块
home项目暴露now模块
```js
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 模块联邦的名称
// 该名称将成为一个全部变量,通过该变量将可获取当前联邦的所有暴露模块
name: 'home',
// 模块联邦生成的文件名,全部变量将置入到该文件中
filename: 'home-entry.js',
// 模块联邦暴露的所有模块
exposes: {
// key相对于模块联邦的路径
// 这里的 ./now 将决定该模块的访问路径为 home/now
// value: 模块的具体路径
'./now': './src/now.js',
},
}),
],
};
```
active项目引入now模块
```js
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 远程使用其他项目暴露的模块
remotes: {
// key: 自定义远程暴露的联邦名
// 比如为 abc 则之后引用该联邦的模块则使用 import "abc/模块名"
// value: 模块联邦名@模块联邦访问地址
// 远程访问时,将从下面的地址加载
home: 'home@http://localhost:8080/home-entry.js',
},
}),
],
};
// src/bootstrap.js
// 远程引入时间模块
import now from 'home/now'
now($('<div>').appendTo(document.body));
```
#### home项目需要使用active项目的news模块
active项目暴露news模块
```js
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 模块联邦的名称
// 该名称将成为一个全部变量,通过该变量将可获取当前联邦的所有暴露模块
name: 'active',
// 模块联邦生成的文件名,全部变量将置入到该文件中
filename: 'active-entry.js',
// 模块联邦暴露的所有模块
exposes: {
// key相对于模块联邦的路径
// 这里的 ./news 将决定该模块的访问路径为 active/news
// value: 模块的具体路径
'./news': './src/news.js',
},
}),
],
};
```
home项目引入news模块
```js
// webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 远程使用其他项目暴露的模块
remotes: {
// key: 自定义远程暴露的联邦名
// 比如为 abc 则之后引用该联邦的模块则使用 import "abc/模块名"
// value: 模块联邦名@模块联邦访问地址
// 远程访问时,将从下面的地址加载
active: 'active@http://localhost:3000/active-entry.js',
}
}),
],
};
// src/bootstrap.js
// 远程引入新闻模块
import news from 'active/news'
news($('<div>').appendTo(document.body));
```
### 处理共享模块
两个项目均使用了jquery为了避免重复可以同时为双方使用`shared`配置共享模块
```js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
plugins: [
new ModuleFederationPlugin({
// 配置共享模块
shared: {
// jquery为共享模块
jquery: {
singleton: true, // 全局唯一
},
},
}),
],
};
```

View File

@ -481,11 +481,11 @@
31. 介绍一下 webpack scope hoisting 31. 介绍一下 webpack scope hoisting
> 直播讲解
> 参考答案: > 参考答案:
> >
> scope hoisting 是 webpack 的内置优化它是针对模块的优化。默认情况下webpack 会将每个模块的代码放置在一个独立的函数环境中,这样是为了保证模块的作用域互不干扰。 > scope hoisting 是 webpack 的内置优化,它是针对模块的优化,在生产环境打包时会自动开启。
>
> 在未开启scope hoisting时webpack 会将每个模块的代码放置在一个独立的函数环境中,这样是为了保证模块的作用域互不干扰。
> >
> 而 scope hoisting 的作用恰恰相反是把多个模块的代码合并到一个函数环境中执行。在这一过程中webpack 会按照顺序正确的合并模块代码,同时对涉及的标识符做适当处理以避免重名。 > 而 scope hoisting 的作用恰恰相反是把多个模块的代码合并到一个函数环境中执行。在这一过程中webpack 会按照顺序正确的合并模块代码,同时对涉及的标识符做适当处理以避免重名。
> >
@ -675,8 +675,6 @@
41. 解释一下 npm 模块安装机制是什么? 41. 解释一下 npm 模块安装机制是什么?
> 直播讲解
> 参考答案: > 参考答案:
> >
> 1. npm 会检查本地的 node_modules 目录中是否已经安装过该模块,如果已经安装,则不再重新安装 > 1. npm 会检查本地的 node_modules 目录中是否已经安装过该模块,如果已经安装,则不再重新安装

View File

@ -1,6 +1,6 @@
<mxfile host="65bd71144e"> <mxfile host="65bd71144e">
<diagram id="QFoU5hE6jV3CWI5dNyLY" name="第 1 页"> <diagram id="QFoU5hE6jV3CWI5dNyLY" name="第 1 页">
<mxGraphModel dx="472" dy="494" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="513" dy="494" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0">
<root> <root>
<mxCell id="0"/> <mxCell id="0"/>
<mxCell id="1" parent="0"/> <mxCell id="1" parent="0"/>
@ -25,15 +25,9 @@
<mxCell id="73" value="01. CMJ和ESM" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1"> <mxCell id="73" value="01. CMJ和ESM" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="167" y="117" width="192" height="31" as="geometry"/> <mxGeometry x="167" y="117" width="192" height="31" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="102" value="postcss" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=16;fontFamily=Roboto Mono;" parent="1" vertex="1">
<mxGeometry x="688" y="570" width="137" height="31" as="geometry"/>
</mxCell>
<mxCell id="104" value="常用构建工具" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;" parent="1" vertex="1"> <mxCell id="104" value="常用构建工具" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;" parent="1" vertex="1">
<mxGeometry x="873" y="512" width="250" height="31" as="geometry"/> <mxGeometry x="873" y="512" width="250" height="31" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="105" value="webpack详细讲解" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;" parent="1" vertex="1">
<mxGeometry x="873" y="570" width="250" height="31" as="geometry"/>
</mxCell>
<mxCell id="111" value="02. 代码质量和风格" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1"> <mxCell id="111" value="02. 代码质量和风格" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="167" y="175" width="192" height="31" as="geometry"/> <mxGeometry x="167" y="175" width="192" height="31" as="geometry"/>
</mxCell> </mxCell>
@ -41,10 +35,19 @@
<mxGeometry x="167" y="233" width="192" height="31" as="geometry"/> <mxGeometry x="167" y="233" width="192" height="31" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="113" value="04. 关于webpack的诸多问题" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1"> <mxCell id="113" value="04. 关于webpack的诸多问题" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="407" y="117" width="254" height="31" as="geometry"/> <mxGeometry x="387" y="117" width="278" height="31" as="geometry"/>
</mxCell> </mxCell>
<mxCell id="116" value="05. webpack5更新了什么" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" vertex="1" parent="1"> <mxCell id="116" value="05. webpack scope hoisting" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="395" y="493" width="254" height="31" as="geometry"/> <mxGeometry x="387" y="175" width="279" height="31" as="geometry"/>
</mxCell>
<mxCell id="117" value="06. webpack5更新了什么" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" parent="1" vertex="1">
<mxGeometry x="387" y="233" width="279" height="31" as="geometry"/>
</mxCell>
<mxCell id="118" value="07. npm模块安装机制" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" vertex="1" parent="1">
<mxGeometry x="690" y="117" width="238" height="31" as="geometry"/>
</mxCell>
<mxCell id="119" value="08. 模块联邦" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontSize=16;fontFamily=Roboto Mono;align=left;spacingLeft=10;" vertex="1" parent="1">
<mxGeometry x="690" y="175" width="238" height="31" as="geometry"/>
</mxCell> </mxCell>
</root> </root>
</mxGraphModel> </mxGraphModel>