ModuleFederationPlugin可以使用模块联邦。使用不同的属性来设置不同的部分mkdir teama
cd teama
npm init -y
mkdir teamb
cd teamb
npm init -y
cd teama
cd teamb
npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev
npm install is-array --save
配置参数
| 字段 | 类型 | 含义 |
|---|---|---|
| name | string | 必传值,即输出的模块名,被远程引用时路径为${name}/${expose} |
| library | object | 声明全局变量的方式,name为umd的name |
| filename | string | 构建输出的文件名 |
| remotes | object | 远程引用的应用名及其别名的映射,使用时以key值作为name |
| exposes | object | 被远程引用时可暴露的资源路径及其别名 |
| shared | object | 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖 |
teamb\webpack.config.js
let path = require("path");
let webpack = require("webpack");
let HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
devtool:false,
entry: "./src/index.js",
output: {
publicPath: "http://localhost:3000/",
//hotUpdateGlobal:'webpackHotUpdate',
//chunkLoadingGlobal: 'webpackChunk'
},
experiments:{
topLevelAwait:true
},
devServer: {
port: 3000
},
plugins: [
new HtmlWebpackPlugin({
template:'./public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "teamb",
exposes: {
"./Dropdown": "./src/Dropdown.js",
"./Button": "./src/Button.js",
},
shared:["is-array"]
})
]
}
teamb\public\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
teamb\src\index.js
import("./bootstrap");
teamb\src\bootstrap.js
import isArray from 'is-array';
let Dropdown = await import('./Dropdown');
let Button = await import('./Button');
console.log(Dropdown.default);
console.log(Button.default);
console.log(isArray.name);
teamb\src\Button.js
import isArray from 'is-array';
export default `(Button[${isArray.name}])`;
teamb\src\Dropdown.js
import isArray from 'is-array';
import ArrowIcon from './ArrowIcon';
export default `(Dropdown[${ArrowIcon}][${isArray.name}])`;
teamb\src\ArrowIcon.js
export default 'ArrowIcon';
teama\webpack.config.js
let path = require("path");
let webpack = require("webpack");
let HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
devtool:false,
output: {
publicPath: "http://localhost:8000/",
hotUpdateGlobal:'webpackHotUpdate'
},
target:['es6','web'],
experiments:{
topLevelAwait:true
},
devServer: {
port: 8000
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new ModuleFederationPlugin({
filename: "remoteEntry.js",
name: "teama",
remotes: {
teamb: "teamb@http://localhost:3000/remoteEntry.js"
},
shared:["is-array"]
})
]
}
teama\src\index.js
import("./bootstrap");
teama\src\bootstrap.js
import HomePage from './HomePage';
console.log(HomePage);
teama\src\HomePage.js
import isArray from 'is-array';
let Dropdown = await import('teamb/Dropdown');
let LoginModal = await import('./LoginModal');
export default `(HomePage[${Dropdown.default}][${LoginModal.default}][${isArray.name}])`
teama\src\LoginModal.js
import isArray from 'is-array';
let Button = await import('teamb/Button');
export default `(LoginModal[${Button.default}][${isArray.name}])`;
window.teamb,他有两个函数属性,init 和 getinit方法用于初始化作用域对象,get 方法用于下载 moduleMap 中导出的远程模块。teamb 到本地模块teamb.init 的执行环境,收集依赖到共享作用域对象 shareScopeteamb.init,初始化作用域对象import 远程模块时调用 teamb.get(moduleName) 通过 JSONP 懒加载远程模块,然后缓存在全局对象 window[‘webpackChunk’ + appName]webpack_require方法读取缓存中的模块,执行用户回调window.teamb = (() => {
var modules = ({
"webpack/container/entry/teamb":
((module, exports, require) => {
var moduleMap = {
"./Dropdown": () => {
return Promise.all([require.e("webpack_sharing_consume_default_is-array_is-array"), require.e("src_Dropdown_js")]).then(() => () => require("./src/Dropdown.js"));
},
"./Button": () => {
return Promise.all([require.e("webpack_sharing_consume_default_is-array_is-array"), require.e("src_Button_js")]).then(() => () => require("./src/Button.js"));
}
};
var get = (module) => {
return moduleMap[module]();
};
var init = (shareScope) => {
var name = "default";
require.S[name] = shareScope;
return require.I(name);
};
require.d(exports, {
get: () => get,
init: () => init
});
})
});
var cache = {};
function require(moduleId) {
if (cache[moduleId]) {
return cache[moduleId].exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
require.n = (module) => {
var getter = module && module.__esModule ?
() => module['default'] :
() => module;
return getter;
};
require.d = (exports, definition) => {
for (var key in definition) {
Object.defineProperty(exports, key, {get: definition[key] });
}
};
require.f = {};
require.e = (chunkId) => {
return Promise.all(Object.keys(require.f).reduce((promises, key) => {
require.f[key](chunkId, promises);
return promises;
}, []));
};
require.u = (chunkId) => {
return "" + chunkId + ".js";
};
require.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
require.l = (url, done) => {
var script = document.createElement('script');
script.src = url;
script.onload = done
document.head.appendChild(script);
};
require.r = (exports) => {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
Object.defineProperty(exports, '__esModule', { value: true });
};
require.S = {};
require.I = (name) => {
if (require.S[name])
return Promise.resolve();
};
require.p = "http://localhost:8080/";
var init = (fn) => function (scopeName, key, version) {
return require.I(scopeName).then(() => {
return fn(require.S[scopeName], key, version);
});
};
var loadShareScope = init((scope, key, version) => {
var versions = scope[key];
var entry = versions[version];
return entry.get()
});
(() => {
var moduleToHandlerMapping = {
"webpack/sharing/consume/default/is-array/is-array": () => loadShareScope("default", "is-array", '1.0.1')
};
var chunkMapping = {
"webpack_sharing_consume_default_is-array_is-array": [
"webpack/sharing/consume/default/is-array/is-array"
]
};
require.f.consumes = (chunkId, promises) => {
if (require.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
let promise = moduleToHandlerMapping[id]().then((factory) => {
modules[id] = (module) => {
module.exports = factory();
}
})
promises.push(promise);
});
}
}
})();
var installedChunks = {
"teamb": 0
};
require.f.j = (chunkId, promises) => {
if ("webpack_sharing_consume_default_is-array_is-array" != chunkId) {
var promise = new Promise((resolve, reject) => {
installedChunks[chunkId] = [resolve, reject];
});
promises.push(promise);
var url = require.p + require.u(chunkId);
require.l(url);
}
};
var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
var [chunkIds, moreModules, runtime] = data;
var moduleId, chunkId, i = 0, resolves = [];
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (require.o(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
for (moduleId in moreModules) {
if (require.o(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (runtime) runtime(require);
if (parentChunkLoadingFunction) parentChunkLoadingFunction(data);
while (resolves.length) {
resolves.shift()();
}
}
var chunkLoadingGlobal = self["webpackChunkteamb"] = self["webpackChunkteamb"] || [];
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
return require("webpack/container/entry/teamb");
})();
teamb\dist\src_Dropdown_js.js
(self["webpackChunkteamb"] = self["webpackChunkteamb"] || []).push([["src_Dropdown_js"], {
"./src/ArrowIcon.js":
((module, exports, require) => {
"use strict";
require.r(exports);
require.d(exports, {
"default": () => DEFAULT_EXPORT
});
const DEFAULT_EXPORT = ('ArrowIcon');
}),
"./src/Dropdown.js":
((module, exports, require) => {
"use strict";
require.r(exports);
require.d(exports, {
"default": () => DEFAULT_EXPORT
});
var is_array_0__ = require("webpack/sharing/consume/default/is-array/is-array");
var is_array_0___default = require.n(is_array_0__);
var _ArrowIcon_1__ = require("./src/ArrowIcon.js");
const DEFAULT_EXPORT = (`(Dropdown[${_ArrowIcon_1__.default}][${(is_array_0___default().name)}])`);
})
}]);
teamb\dist\src_Button_js.js
(self["webpackChunkteamb"] = self["webpackChunkteamb"] || []).push([["src_Button_js"], {
"./src/Button.js":
((module, exports, require) => {
require.r(exports);
require.d(exports, {
"default": () => DEFAULT_EXPORT
});
var is_array_0__ = require("webpack/sharing/consume/default/is-array/is-array");
var is_array_0___default = require.n(is_array_0__);
const DEFAULT_EXPORT = (`(Button[${(is_array_0___default().name)}])`);
})
}]);
/**
console.log(require.cache);
./src/ArrowIcon.js: {exports: Module}
./src/Button.js: {exports: Module}
./src/Dropdown.js: {exports: Module}
webpack/container/entry/teamb: {exports: {…}}
webpack/sharing/consume/default/is-array/is-array: {exports: ƒ}
*/
teama\dist\main.js
(() => {
//模块定义
var modules = ({
"webpack/container/reference/teamb":((module, exports, require) => {
//加载远程脚本,返回 window.teamb
module.exports = new Promise((resolve) => {
require.l("http://localhost:8080/remoteEntry.js", resolve);
}).then(() => window.teamb);
})
});
var cache = {};
function require(moduleId) {
if (cache[moduleId]) {
return cache[moduleId].exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
//如果在ES module,取default,否则取自己
require.n = (module) => {
var getter = module && module.__esModule ?
() => module['default'] :
() => module;
return getter;
};
require.d = (exports, definition) => {
for (var key in definition) {
Object.defineProperty(exports, key, {get: definition[key]});
}
};
require.u = (chunkId) => {
return "" + chunkId + ".js";
};
require.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
require.l = (url, done) => {
var script = document.createElement('script');
script.src = url;
script.onload = done
document.head.appendChild(script);
};
require.r = (exports) => {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
Object.defineProperty(exports, '__esModule', { value: true });
};
require.f = {};
require.e = (chunkId) => {
return Promise.all(Object.keys(require.f).reduce((promises, key) => {
require.f[key](chunkId, promises);
return promises;
}, []));
};
(() => {
//远程模块的代码码映射
var chunkMapping = {
"webpack_container_remote_teamb_Dropdown": [
"webpack/container/remote/teamb/Dropdown"
],
"webpack_container_remote_teamb_Button": [
"webpack/container/remote/teamb/Button"
]
};
var idToExternalAndNameMapping = {
"webpack/container/remote/teamb/Dropdown": [
"default",//命名空间
"./Dropdown",//导出的名称
"webpack/container/reference/teamb"//来自哪个外部模块
],
"webpack/container/remote/teamb/Button": [
"default",
"./Button",
"webpack/container/reference/teamb"
]
};
require.f.remotes = (chunkId, promises) => {
if (require.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
var [scopeName,remoteExposeName,remoteId] = idToExternalAndNameMapping[id];
let promise = require(remoteId).then(external => {
return require.I(scopeName).then(() => {
return external.get(remoteExposeName).then(factory => {
modules[id] = (module) => {
module.exports = factory();
}
});
});
});
promises.push(promise);
});
}
}
})();
//存放scope
require.S = {};
//初始化scope
require.I = (name) => {
if(require.S[name])
return Promise.resolve();
var scope = require.S[name] = {};
//注册共享模块
var register = (name, version, factory) => {
var currentScope = scope[name] = scope[name] || {};
currentScope[version] = { get: factory};
};
var promises = [];
//初始化远程外部模块
var initExternal = (id) => {
var module = require(id);
let promise = module.then(module=>module.init(scope));
promises.push(promise);
}
//scope的名称
switch (name) {
case "default": {
register("is-array", "1.0.1", () => require.e("node_modules_is-array_index_js").then(() => () => require("./node_modules/is-array/index.js")));
initExternal("webpack/container/reference/teamb");
}
break;
}
return Promise.all(promises)
};
require.p = "http://localhost:8081/";
var init = (fn) => function (scopeName, key, version) {
return require.I(scopeName).then(() => {
return fn(require.S[scopeName], key, version);
});
};
var loadShareScope = init((scope, key, version) => {
var versions = scope[key];
var entry = versions[version];
return entry.get()
});
(() => {
//share scope consumed modules
var moduleToHandlerMapping = {
"webpack/sharing/consume/default/is-array/is-array": () => loadShareScope("default", "is-array", '1.0.1')
};
var chunkMapping = {
"src_bootstrap_js": [
"webpack/sharing/consume/default/is-array/is-array"
]
};
require.f.consumes = (chunkId, promises) => {
if (require.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) => {
let promise = moduleToHandlerMapping[id]().then((factory) => {
modules[id] = (module) => {
module.exports = factory();
}
})
promises.push(promise);
});
}
}
})();
var installedChunks = {
"main": 0
};
require.f.j = (chunkId, promises) => {
if (!/^webpack_container_remote_teamb_(Button|Dropdown)$/.test(chunkId)) {
var promise = new Promise((resolve, reject) => {
installedChunks[chunkId] = [resolve, reject];
});
promises.push(promise);
var url = require.p + require.u(chunkId);
require.l(url);
}
};
var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
var [chunkIds, moreModules] = data;
var moduleId, chunkId, i = 0, resolves = [];
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (require.o(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
for (moduleId in moreModules) {
if (require.o(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (parentChunkLoadingFunction) parentChunkLoadingFunction(data);
while (resolves.length) {
resolves.shift()();
}
}
var chunkLoadingGlobal = self["webpackChunkteama"] = self["webpackChunkteama"] || [];
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
require.e("src_bootstrap_js").then(require.bind(require, "./src/bootstrap.js"));
})();
teama\dist\src_bootstrap_js.js
(self["webpackChunkteama"] = self["webpackChunkteama"] || []).push([["src_bootstrap_js"], {
"./src/HomePage.js":
((module, exports, require) => {
"use strict";
module.exports = (async () => {
require.r(exports);
require.d(exports, {
"default": () => DEFAULT_EXPORT
});
var is_array_0__ = require("webpack/sharing/consume/default/is-array/is-array");
var is_array_0___default = require.n(is_array_0__);
let Dropdown = await require.e("webpack_container_remote_teamb_Dropdown").then(require.bind(require, "webpack/container/remote/teamb/Dropdown"));
let LoginModal = await require.e("src_LoginModal_js").then(require.bind(require, "./src/LoginModal.js"));
const DEFAULT_EXPORT = (`(HomePage[${Dropdown.default}][${LoginModal.default}][${(is_array_0___default().name)}])`);
return exports;
})();
}),
"./src/bootstrap.js":
((module, exports, require) => {
"use strict";
module.exports = (async () => {
require.r(exports);
var _HomePage_0__ = require("./src/HomePage.js");
_HomePage_0__ = await Promise.resolve(_HomePage_0__);
console.log(_HomePage_0__.default);
return exports;
})();
})
}]);
teama\dist\src_LoginModal_js.js
(self["webpackChunkteama"] = self["webpackChunkteama"] || []).push([["src_LoginModal_js"], {
"./src/LoginModal.js":
((module, exports, require) => {
"use strict";
module.exports = (async () => {
require.r(exports);
require.d(exports, {
"default": () => DEFAULT_EXPORT
});
var is_array_0__ = require("webpack/sharing/consume/default/is-array/is-array");
var is_array_0___default = require.n(is_array_0__);
let Button = await require.e("webpack_container_remote_teamb_Button").then(require.bind(require, "webpack/container/remote/teamb/Button"));
const DEFAULT_EXPORT = (`(LoginModal[${Button.default}][${(is_array_0___default().name)}])`);
return exports;
})();
})
}]);
teama\dist\node_modules_is-array_index_js.js
(self["webpackChunkteama"] = self["webpackChunkteama"] || []).push([["node_modules_is-array_index_js"], {
"./node_modules/is-array/index.js":
((module) => {
console.log('双方共享的是teama的isArray');
var isArray = Array.isArray;
var str = Object.prototype.toString;
module.exports = isArray || function (val) {
return !!val && '[object Array]' == str.call(val);
};
})
}]);