接 Webpack 打包 commonjs 和 esmodule 模块的产物对比 我们来继续分析。这篇文章主要来看一下动态引入,允许我们引入的模块名包含变量。
⚠️超长代码预警,需要几个小时的时间去啃,但读懂以后应该会很开心。
commonjs
新建一个 json
文件夹,包含几个 json
文件,和一个 add
方法。
其中 add.js
就是一个简单的加法模块。
// src/commonjs/json/add.js
console.log("add开始引入");
module.exports.add = (a, b) => {
return a b;
};
test1.json
和 test2.json
都是一个 json
对象。
// src/commonjs/json/test1.json
{
"data": "test1"
}
// src/commonjs/json/test2.json
{
"data": "test2"
}
然后我们提供一个 hello
模块,可以根据用户传入的参数,来引入不同的 json
文件返回给用户。
module.exports = function (filename) {
const data = require("./json/" filename ".json");
return data;
};
需要注意的上边 require
传入的模块名一定不能是一个纯变量,比如 require(filename)
,不然 webpack
就不知道该打包哪些文件了。
上边我们限定了目录位置 ./json
和文件名后缀 .json
。这样 Webpack
就会把 json
文件夹下所有的 .json
文件进行打包。
主函数 index.js
来调用 hello
方法。
console.log("commonjs开始执行");
const hello = require("./hello");
console.log(hello("test1"));
可以看一下控制台是正常输出:
image-20220503173736921
看一下打包产物:
主要看一下保存所有模块的 __webpack_modules__
变量,其它的可以看一下上篇 Webpack 打包 commonjs 和 esmodule 模块的产物对比 。
var __webpack_modules__ = {
"./src/commonjs/hello.js": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
module.exports = function (filename) {
const data = __webpack_require__(
"./src/commonjs/json sync recursive ^\.\/.*\.json$"
)("./" filename ".json");
return data;
};
},
"./src/commonjs/json sync recursive ^\.\/.*\.json$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
...
},
"./src/commonjs/json/test1.json": (module) => {
"use strict";
module.exports = { data: "test1" };
},
"./src/commonjs/json/test2.json": (module) => {
"use strict";
module.exports = { data: "test2" };
},
};
主要是四个模块 ./src/commonjs/hello.js
、./src/commonjs/json sync recursive ^\.\/.*\.json$
、./src/commonjs/json/test1.json
和 ./src/commonjs/json/test2.json
。
./src/commonjs/json/test1.json
和 ./src/commonjs/json/test2.json
这两个模块就是把我们的 json
文件用 module.exports
来导出。
./src/commonjs/hello.js
模块中先调用 ./src/commonjs/json sync recursive ^\.\/.*\.json$
模块的方法,再进行传参。
此外将我们原本的 "./json/" filename ".json"
参数转为了 "./" filename ".json"
。
重点来看下 ./src/commonjs/json sync recursive ^\.\/.*\.json$
,详见下边的注释
"./src/commonjs/json sync recursive ^\.\/.*\.json$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
// 映射 key
var map = {
"./test1.json": "./src/commonjs/json/test1.json",
"./test2.json": "./src/commonjs/json/test2.json",
};
function webpackContext(req) {
var id = webpackContextResolve(req); // 得到映射后的 key
return __webpack_require__(id); // 通过 __webpack_require__ 导入文件
}
// 返回映射后的 key
function webpackContextResolve(req) {
if (!__webpack_require__.o(map, req)) {
var e = new Error("Cannot find module '" req "'");
e.code = "MODULE_NOT_FOUND";
throw e;
}
return map[req];
}
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id =
"./src/commonjs/json sync recursive ^\.\/.*\.json$";
},
commonjs
模块整体上就是把匹配 "./json/" filename ".json"
这个格式的文件 test1.json
和 test2.json
都进行了打包,并且略过了 add.js
文件。
可以再看下整体的产物:
代码语言:javascript复制(() => {
var __webpack_modules__ = {
"./src/commonjs/hello.js": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
module.exports = function (filename) {
const data = __webpack_require__(
"./src/commonjs/json sync recursive ^\.\/.*\.json$"
)("./" filename ".json");
return data;
};
},
"./src/commonjs/json sync recursive ^\.\/.*\.json$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
var map = {
"./test1.json": "./src/commonjs/json/test1.json",
"./test2.json": "./src/commonjs/json/test2.json",
};
function webpackContext(req) {
var id = webpackContextResolve(req);
return __webpack_require__(id);
}
function webpackContextResolve(req) {
if (!__webpack_require__.o(map, req)) {
var e = new Error("Cannot find module '" req "'");
e.code = "MODULE_NOT_FOUND";
throw e;
}
return map[req];
}
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id =
"./src/commonjs/json sync recursive ^\.\/.*\.json$";
},
"./src/commonjs/json/test1.json": (module) => {
"use strict";
module.exports = { data: "test1" };
},
"./src/commonjs/json/test2.json": (module) => {
"use strict";
module.exports = { data: "test2" };
},
};
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](
module,
module.exports,
__webpack_require__
);
return module.exports;
}
(() => {
__webpack_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
})();
var __webpack_exports__ = {};
(() => {
console.log("commonjs开始执行");
const hello = __webpack_require__("./src/commonjs/hello.js");
console.log(hello("test1"));
})();
})();
esmodule
esmodule
提供了 import()
方法进行动态引入,会返回一个 Promise
对象。
★The ES2015 Loader spec defines
import()
as method to load ES2015 modules dynamically on runtime. ”
我们来用 esmodule
的形式改写下上边 commonjs
的代码。
首先是 hello.js
。
// src/esmodule/hello.js
const hello = (filename) => {
return import("./json/" filename ".json");
};
export default hello;
然后是 index.js
// src/esmodule/index.js
console.log("esmodule开始执行");
import hello from "./hello";
hello("test1").then((data) => {
console.log(data);
});
不同于 commonjs
,除了输出 test1.json
原本的数据,还多了一个 default
属性。
image-20220503191214724
打包文件中除了 main.js
,把两个 json
文件也单拎了出来,如下图:
image-20220503214502914
打包产物中除了 Webpack 打包 commonjs 和 esmodule 模块的产物对比 介绍的 d、o、r
方法,又多了很多奇奇怪怪的方法。
m
属性指向 __webpack_modules__
,保存了导出的所有模块。
var __webpack_modules__ = {
"./src/esmodule/hello.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
default: () => __WEBPACK_DEFAULT_EXPORT__,
});
const hello = (filename) => {
return __webpack_require__(
"./src/esmodule/json lazy recursive ^\.\/.*\.json$"
)("./" filename ".json");
};
const __WEBPACK_DEFAULT_EXPORT__ = hello;
},
"./src/esmodule/json lazy recursive ^\.\/.*\.json$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
...
},
};
__webpack_require__.m = __webpack_modules__;
g
属性指向全局对象,浏览器中的话就会返回 window
。
__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;
}
})();
u
方法是将 chunkId
末尾加上 .main.js
,主要是为了和打包出来的文件名吻合。
__webpack_require__.u = (chunkId) => {
return "" chunkId ".main.js";
};
p
属性主要是为了拿到域名,开始执行的时候浏览器会加载我们的 main.js
。
image-20220503214502914
当前请求的地址是 http://127.0.0.1:5501/dist/main.js
,通过这个地址,我们要拿到 http://127.0.0.1:5501/dist/
,详见下边的代码:
var scriptUrl;
if (__webpack_require__.g.importScripts) // 这里不执行
scriptUrl = __webpack_require__.g.location "";
var document = __webpack_require__.g.document; // 这里拿到 window.document
if (!scriptUrl && document) {
if (document.currentScript) scriptUrl = document.currentScript.src; // 这里得到 http://127.0.0.1:5501/dist/main.js
if (!scriptUrl) { // 这里不执行
var scripts = document.getElementsByTagName("script");
if (scripts.length) scriptUrl = scripts[scripts.length - 1].src;
}
}
if (!scriptUrl)
throw new Error(
"Automatic publicPath is not supported in this browser"
);
scriptUrl = scriptUrl
.replace(/#.*$/, "")
.replace(/?.*$/, "")
.replace(//[^/] $/, "/"); // 这里得到 http://127.0.0.1:5501/dist/
__webpack_require__.p = scriptUrl;
接下来会比较复杂,会分成 8
个步骤来看一下 esmodule
异步加载的主流程。整体思路是通过 JSONP
的形式发送请求加载我们的 JSON
文件,同时把整个的加载过程会包装为一个 Promise
,加载完成将内容保存到 __webpack_modules__
中。
hello
方法通过 __webpack_require__
调用 "./src/esmodule/json lazy recursive ^\.\/.*\.json$"
方法。
"./src/esmodule/hello.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
default: () => __WEBPACK_DEFAULT_EXPORT__,
});
const hello = (filename) => {
return __webpack_require__(
"./src/esmodule/json lazy recursive ^\.\/.*\.json$"
)("./" filename ".json");
};
const __WEBPACK_DEFAULT_EXPORT__ = hello;
},
./src/esmodule/json lazy recursive ^\.\/.*\.json$
方法导出的是 webpackAsyncContext
方法。
"./src/esmodule/json lazy recursive ^\.\/.*\.json$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
var map = {
"./test1.json": [
"./src/esmodule/json/test1.json",
"src_esmodule_json_test1_json",
],
"./test2.json": [
"./src/esmodule/json/test2.json",
"src_esmodule_json_test2_json",
],
};
function webpackAsyncContext(req) {
if (!__webpack_require__.o(map, req)) {
return Promise.resolve().then(() => {
var e = new Error("Cannot find module '" req "'");
e.code = "MODULE_NOT_FOUND";
throw e;
});
}
debugger;
var ids = map[req],
id = ids[0];
return __webpack_require__.e(ids[1]).then(() => {
return __webpack_require__.t(id, 3 | 16);
});
}
webpackAsyncContext.keys = () => Object.keys(map);
webpackAsyncContext.id =
"./src/esmodule/json lazy recursive ^\.\/.*\.json$";
module.exports = webpackAsyncContext;
map
中定义了 json
文件的映射,"./src/esmodule/json/test1.json"
是原本的文件位置,会作为模块的 key
,"src_esmodule_json_test1_json"
对应打包后的文件名。
image-20220503214502914
看一下 webpackAsyncContext
方法,先调用 __webpack_require__.e
方法来发送请求加载文件并且返回一个 Promise
。__webpack_require__.t
方法会将返回的数据加一个 default
属性,也就是开头说的一个不同之处。
function webpackAsyncContext(req) {
if (!__webpack_require__.o(map, req)) {
return Promise.resolve().then(() => {
var e = new Error("Cannot find module '" req "'");
e.code = "MODULE_NOT_FOUND";
throw e;
});
}
var ids = map[req], // ids[0] 是原本路径, id[1] 是打包后的文件名字
id = ids[0];
return __webpack_require__.e(ids[1]).then(() => {
return __webpack_require__.t(id, 3 | 16);
});
}
详细看一下 __webpack_require__.e
方法,传入了一个参数 chunkId
,这里就是 src_esmodule_json_test1_json
。
__webpack_require__.e = (chunkId) => {
return Promise.all(
Object.keys(__webpack_require__.f).reduce((promises, key) => {
__webpack_require__.f[key](chunkId, promises);
return promises;
}, [])
);
};
主要就是执行 f
对象的所有属性函数,f
的属性函数会在传入的 promises
中添加当前的 Promise
。
看一下 f
对象的属性函数的定义。
f
对象当前场景下只有一个 j
属性函数,所以在上边的 e
方法中会执行下边的 j
函数。
var installedChunks = { // 记录加载的文件
main: 0,
};
__webpack_require__.f.j = (chunkId, promises) => {
var installedChunkData = __webpack_require__.o( // o 方法是判断当前对象是否有该属性
installedChunks,
chunkId
)
? installedChunks[chunkId]
: undefined;
if (installedChunkData !== 0) {
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
if (true) {
// 第一次加载文件会走到这里
var promise = new Promise(
(resolve, reject) =>
(installedChunkData = installedChunks[chunkId] =
[resolve, reject]) // 将 resolve 和 reject 保存
);
promises.push((installedChunkData[2] = promise)); // 把当前 promise 塞入到传入的 promises 数组
var url =
__webpack_require__.p
__webpack_require__.u(chunkId); // url 拼成了 http://127.0.0.1:5501/dist/src_esmodule_json_test1_json.main.js
var error = new Error();
var loadingEnded = (event) => {
if (
__webpack_require__.o(installedChunks, chunkId)
) {
...
}
}
};
__webpack_require__.l(
url,
loadingEnded,
"chunk-" chunkId,
chunkId
);
} else installedChunks[chunkId] = 0;
}
}
};
上边的 j
函数执行完后,会在 installedChunks
对象中增加一个 src_esmodule_json_test1_json
的 key
,值是一个数组,数组的 0
是 promise
的 resolve
,1
是 promise
的 reject
,2
是当前 promise
,如下图所示。
image-20220504181410631
最后执行 l
方法,就是我们的主角,通过 JSONP
的形式,塞一个 script
去加载 http://127.0.0.1:5501/dist/src_esmodule_json_test1_json.main.js
文件。
加载完成或者加载错误会执行上边的 loadingEnded
方法。
var error = new Error();
var loadingEnded = (event) => {
if (
__webpack_require__.o(installedChunks, chunkId)
) {
installedChunkData = installedChunks[chunkId];
if (installedChunkData !== 0)
installedChunks[chunkId] = undefined;
if (installedChunkData) { // 走到这里 installedChunkData 应该已经是 0 了(后边会讲到哪里置的 0),不然的话就抛出错误
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); // installedChunkData[1] 是之前保存的 reject
}
}
};
看一下 l
方法。
var inProgress = {};
var dataWebpackPrefix = "webpack-demo:";
__webpack_require__.l = (url, done, key, chunkId) => {
if (inProgress[url]) {
inProgress[url].push(done);
return;
}
var script, needAttach;
...
// 设置 script
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) => {
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); // 插入当前 script
};
主要就是 scrpit
加载完毕后的回调,然后将当前 script
插入到 head
标签中。
image-20220504183052534
接着浏览器就会发送请求加载我们之前打包后的 js
文件。
image-20220504183143512
看一下文件内容:
代码语言:javascript复制"use strict";
(self["webpackChunkwebpack_demo"] =
self["webpackChunkwebpack_demo"] || []).push([
["src_esmodule_json_test1_json"],
{
"./src/esmodule/json/test1.json": (module) => {
module.exports = { data: "test1" };
},
},
]);
加载完毕后会执行上边的代码,self["webpackChunkwebpack_demo"]
的 push
方法之前已经重定义好了,也就是下边的代码。
var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
...
};
var chunkLoadingGlobal = (self["webpackChunkwebpack_demo"] =
self["webpackChunkwebpack_demo"] || []);
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind( // 定义 push 方法
null,
chunkLoadingGlobal.push.bind(chunkLoadingGlobal)
);
执行 self["webpackChunkwebpack_demo"] || []).push
相当于执行 webpackJsonpCallback
方法。
var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
var [chunkIds, moreModules, runtime] = data;
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[chunkIds[i]] = 0;
}
};
传入的 data
参数就是加载的文件内容时候传入的,也就是下边的样子。
[
["src_esmodule_json_test1_json"],
{
"./src/esmodule/json/test1.json": (module) => {
module.exports = { data: "test1" };
},
},
]
webpackJsonpCallback
拿到上边的 data
后主要做了三件事情:
- 将
./src/esmodule/json/test1.json
模块保存到__webpack_modules__
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__);
}
__webpack_require__.m
就是__webpack_modules__
,保存着所有模块的键值对。 - 将
installedChunks
之前保存的promise
执行resolve
。for (; i < chunkIds.length; i ) {
chunkId = chunkIds[i];
if (
__webpack_require__.o(installedChunks, chunkId) &&
installedChunks[chunkId]
) {
installedChunks[chunkId][0](); // 数组 0 保存的就是 resolve
}
}
- 将
installedChunks
相应的对象置为0
,代表加载完成了,前边讲的loadingEnded
会判断这里是不是0
。installedChunks[chunkIds[i]] = 0;
上边一大堆完成了 JSONP
,并且成功将动态加载的模块放到了 __webpack_modules__
中,然后我们看一下执行到哪里了:
function webpackAsyncContext(req) {
if (!__webpack_require__.o(map, req)) {
return Promise.resolve().then(() => {
var e = new Error("Cannot find module '" req "'");
e.code = "MODULE_NOT_FOUND";
throw e;
});
}
var ids = map[req], // ids[0] 是原本路径, id[1] 是打包后的文件名字
id = ids[0];
return __webpack_require__.e(ids[1]).then(() => {
return __webpack_require__.t(id, 3 | 16);
});
}
执行完 e
方法,接下执行 t
方法,会有很多不同的 mode
进入不同的分支,这里就不细究了,只需要知道最终结果是把数据加了 default
属性然后返回。
__webpack_require__.t = function (value, mode) {
if (mode & 1) value = this(value);
if (mode & 8) return value;
if (typeof value === "object" && value) {
if (mode & 4 && value.__esModule) return value;
if (mode & 16 && typeof value.then === "function") return value;
}
var ns = Object.create(null);
__webpack_require__.r(ns);
var def = {};
leafPrototypes = leafPrototypes || [
null,
getProto({}),
getProto([]),
getProto(getProto),
];
for (
var current = mode & 2 && value;
typeof current == "object" && !~leafPrototypes.indexOf(current);
current = getProto(current)
) {
Object.getOwnPropertyNames(current).forEach(
(key) => (def[key] = () => value[key])
);
}
def["default"] = () => value;
__webpack_require__.d(ns, def);
return ns;
};
拿数据的话就是第一行代码,if (mode & 1) value = this(value);
,这里的 this
就是 webpack_require
函数,相当于执行 __webpack_require__('./src/esmodule/json/test1.json')
。关于 this
指向可以参考 JavaScript中this指向详细分析(译)。
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](
module,
module.exports,
__webpack_require__
);
return module.exports;
}
'./src/esmodule/json/test1.json'
之前已经保存到了 __webpack_modules__
中,所以就把之前加载的内容返回给了 value
。
上边讲了 hello
方法的执行,最后返回了一个包含数据的 promise
,最终回到了我们的 index
函数中。
var _hello__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
"./src/esmodule/hello.js"
);
console.log("esmodule开始执行");
(0, _hello__WEBPACK_IMPORTED_MODULE_0__["default"])("test1").then(
(data) => {
console.log(data);
}
);
以上就是 esmodule
异步加载模块的全过程了,稍微有些复杂,整体流程如下:
定义 JSOP
的回调函数((self["webpackChunkwebpack_demo"].push
) ->
进入 index
函数 -> 进入 hello
函数 -> 进入 webpackAsyncContext
函数 ->
进入 __webpack_require__.e
函数 ->
执行 __webpack_require__.f.j
函数,保存 promise
,生成要下载的文件 url
->
进入 __webpack_require__.l
函数,运用 JSONP
,动态插入 script
->
加载 script
文件,执行回调函数 (self["webpackChunkwebpack_demo"].push
,将数据保存到 __webpack_modules__
->
执行 __webpack_require__.t
方法,将数据加上 default
返回 ->
hello
函数执行完毕 ->
回到 index
函数继续执行,输出导入的数据。
可以再看下完整代码:
代码语言:javascript复制(() => {
var __webpack_modules__ = {
"./src/esmodule/hello.js": (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
default: () => __WEBPACK_DEFAULT_EXPORT__,
});
const hello = (filename) => {
return __webpack_require__(
"./src/esmodule/json lazy recursive ^\.\/.*\.json$"
)("./" filename ".json");
};
const __WEBPACK_DEFAULT_EXPORT__ = hello;
},
"./src/esmodule/json lazy recursive ^\.\/.*\.json$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
var map = {
"./test1.json": [
"./src/esmodule/json/test1.json",
"src_esmodule_json_test1_json",
],
"./test2.json": [
"./src/esmodule/json/test2.json",
"src_esmodule_json_test2_json",
],
};
function webpackAsyncContext(req) {
if (!__webpack_require__.o(map, req)) {
return Promise.resolve().then(() => {
var e = new Error("Cannot find module '" req "'");
e.code = "MODULE_NOT_FOUND";
throw e;
});
}
debugger;
var ids = map[req],
id = ids[0];
return __webpack_require__.e(ids[1]).then(() => {
return __webpack_require__.t(id, 3 | 16);
});
}
webpackAsyncContext.keys = () => Object.keys(map);
webpackAsyncContext.id =
"./src/esmodule/json lazy recursive ^\.\/.*\.json$";
module.exports = webpackAsyncContext;
},
};
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](
module,
module.exports,
__webpack_require__
);
return module.exports;
}
__webpack_require__.m = __webpack_modules__;
(() => {
var getProto = Object.getPrototypeOf
? (obj) => Object.getPrototypeOf(obj)
: (obj) => obj.__proto__;
var leafPrototypes;
__webpack_require__.t = function (value, mode) {
if (mode & 1) value = this(value);
if (mode & 8) return value;
if (typeof value === "object" && value) {
if (mode & 4 && value.__esModule) return value;
if (mode & 16 && typeof value.then === "function") return value;
}
var ns = Object.create(null);
__webpack_require__.r(ns);
var def = {};
leafPrototypes = leafPrototypes || [
null,
getProto({}),
getProto([]),
getProto(getProto),
];
for (
var current = mode & 2 && value;
typeof current == "object" && !~leafPrototypes.indexOf(current);
current = getProto(current)
) {
Object.getOwnPropertyNames(current).forEach(
(key) => (def[key] = () => value[key])
);
}
def["default"] = () => value;
__webpack_require__.d(ns, def);
return ns;
};
})();
(() => {
__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_require__.f = {};
__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__.u = (chunkId) => {
return "" chunkId ".main.js";
};
})();
(() => {
__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_require__.o = (obj, prop) =>
Object.prototype.hasOwnProperty.call(obj, prop);
})();
(() => {
var inProgress = {};
var dataWebpackPrefix = "webpack-demo:";
__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) => {
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_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module",
});
}
Object.defineProperty(exports, "__esModule", { value: true });
};
})();
(() => {
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;
}
}
if (!scriptUrl)
throw new Error(
"Automatic publicPath is not supported in this browser"
);
scriptUrl = scriptUrl
.replace(/#.*$/, "")
.replace(/?.*$/, "")
.replace(//[^/] $/, "/");
__webpack_require__.p = scriptUrl;
})();
(() => {
var installedChunks = {
main: 0,
};
__webpack_require__.f.j = (chunkId, promises) => {
var installedChunkData = __webpack_require__.o(
installedChunks,
chunkId
)
? installedChunks[chunkId]
: undefined;
if (installedChunkData !== 0) {
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
if (true) {
var promise = new Promise(
(resolve, reject) =>
(installedChunkData = installedChunks[chunkId] =
[resolve, reject])
);
promises.push((installedChunkData[2] = promise));
var url =
__webpack_require__.p
__webpack_require__.u(chunkId);
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;
}
}
};
var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
var [chunkIds, moreModules, runtime] = data;
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[chunkIds[i]] = 0;
}
};
var chunkLoadingGlobal = (self["webpackChunkwebpack_demo"] =
self["webpackChunkwebpack_demo"] || []);
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(
null,
chunkLoadingGlobal.push.bind(chunkLoadingGlobal)
);
})();
var __webpack_exports__ = {};
(() => {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _hello__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
"./src/esmodule/hello.js"
);
console.log("esmodule开始执行");
(0, _hello__WEBPACK_IMPORTED_MODULE_0__["default"])("test1").then(
(data) => {
console.log(data);
}
);
})();
})();
总
require
引入模块是同步的,因此打包的时候就将数据保存起来了,打包产物也比较简单。
import()
是异步的,需要异步加载的文件提前单独生成文件,然后通过 JSONP
的形式进行加载,加载完毕后通过回调将数据添加到 __webpack_modules__
对象中,方便后续使用。