1 require
1.1用法
//index.js
const add = require('./a.js')
console.log(add(2,5))
//a.js
const add = function (a, b) {
return a + b
}
module.exports = add
1.2打包后代码
(() => {
// webpackBootstrap
var __webpack_modules__ = {
"./src/a.js": (module) => {
eval(
"var add = function add(a, b) {\n return a + b;\n};\nmodule.exports = add;\n\n//# sourceURL=webpack://my-webpack-project/./src/a.js?"
);
},
"./src/index.js": (
__unused_webpack_module,
__unused_webpack_exports,
__webpack_require__
) => {
eval(
'var add = __webpack_require__(/*! ./a.js */ "./src/a.js");\nconsole.log(add(2, 5));\n\n//# sourceURL=webpack://my-webpack-project/./src/index.js?'
);
},
};
/************************************************************************/
// The module cache
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;
}
/************************************************************************/
var __webpack_exports__ = __webpack_require__("./src/index.js");
})();
1.3 分析
__webpack_modules__
使用立即执行函数将 编译后的源代码 存储在 __webpack_modules__
变量,格式为
__webpack_modules__ = {
文件1路径:(
module,
exports,
require
) => {
eval(源代码字符串,);
}
...
}
__webpack_require__
webpack 封装的require方法
- webpack_module_cache[moduleId] = {exports: {源代码对象},},已经加载了直接返回
- 根据 moduleId 即
文件名
作为缓存的key __webpack_modules__[moduleId](module, module.exports, __webpack_require__)
执行源代码模块,因为每个reqiure的模块都往exports对象上赋值了,所有也就是挂载到了全局__webpack_module_cache__
变量上
2 import 静态加载
2.1用法
//index.js
import add from './a.js'
console.log(add(2,5))
//a.js
const add = function (a, b) {
return a + b
}
export default add
打包后代码dist目录下只有一个文件 index.js
2.2 打包后代码
(() => {
// webpackBootstrap
var __webpack_modules__ = {
"./src/a.js":
(__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nvar add = function add(a, b) {\n return a + b;\n};\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (add);\n\n//# sourceURL=webpack://my-webpack-project/./src/a.js?'
);
},
"./src/index.js":
(__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval(
'__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _a_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a.js */ "./src/a.js");\n\nconsole.log((0,_a_js__WEBPACK_IMPORTED_MODULE_0__["default"])(2, 5));\n\n//# sourceURL=webpack://my-webpack-project/./src/index.js?'
);
},
};
/************************************************************************/
// The module cache
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/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 */
(() => {
__webpack_require__.r = (exports) => {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
}
Object.defineProperty(exports, "__esModule", { value: true });
};
})();
// startup
var __webpack_exports__ = __webpack_require__("./src/index.js");
})();
2.3 分析
📌 相同:
可以看出 webpack在编译 import和require后的代码逻辑差不多,都是使用自己封装的__webpack_require__方法
区别:
多了__webpack_require__.d、__webpack_require__.o、__webpack_require__.r
几个方法
__webpack_require__.d
将esm export的对象
挂载在modules.exports
,其实就抹平了与require的区别__webpack_require__.o
公共方法:执行对象原型上的方法__webpack_require__.r
给import的exports对象增加__esModule属性,用以区分是否为import导入
3 import 动态加载 -文件名为常量
3.1用法
//index.js
import('./bundle.js').then((add) => {
console.log(add(1+2))
})
//bundle.js
const add = function (a, b) {
return a + b
}
export default add
打包后代码dist目录下分两个文件 index.js 和 0.bundle.js
3.2 打包后核心代码
(1)index.js
(function(modules){
var installedModules = {};
function __webpack_require__(moduleId) {
...
}
...
function webpackJsonpCallback(data){...}
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback; //---异步加载用到
...
return __webpack_require__(__webpack_require__.s = './index.js');
})
({
'./index.js':(function(module, exports, __webpack_require__) {
__webpack_require__.e(/*! import() */ 0)
.then(__webpack_require__.bind(null,"./bundle.js"))
.then((add) => {
console.log(add(1+2))
})")
})
});
webpack_require.e 函数 返回一个Promise.all
installedChunks[chunkId]:
- 0 已经加载;
- undefined记载失败;
- [resolve, reject,promise]正在加载
// 当前模块依赖的chuncks子模块
var installedChunks = {
a: 0
};
__webpack_require__.e = function requireEnsure(chunkId) {
var promises = [];
var installedChunkData = installedChunks[chunkId];
if(installedChunkData !== 0) {
if(installedChunkData) {
promises.push(installedChunkData[2]);
} else {
// step 1,新建一个promise
var promise = new Promise(function(resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];});
promises.push(installedChunkData[2] = promise);
// step2,新增script标签加载 import里面的文件
var script = document.createElement('script');
var onScriptComplete;
script.charset = 'utf-8';
script.timeout = 120;
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
script.src = jsonpScriptSrc(chunkId);
document.head.appendChild(script);
// step3,监听script.onerror和定时120s后,执行reject
var error = new Error();
onScriptComplete = function (event) {
script.onerror = script.onload = null;
clearTimeout(timeout);
var chunk = installedChunks[chunkId];
if(chunk !== 0) {
if(chunk) {
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;
// chunk[1] = reject
chunk[1](error);
}
installedChunks[chunkId] = undefined;
}
};
var timeout = setTimeout(function(){
onScriptComplete({ type: 'timeout', target: script });
}, 120000);
script.onerror = script.onload = onScriptComplete;
}
}
return Promise.all(promises);
};
(2) 0.bundle.js
script.src = jsonpScriptSrc(chunkId) 相当于执行 webpackJsonpCallback
加载异步js文件
webpackJsonp.push([chunkIds=[0],moreModules={"./bundle.js":(function(){/*立即执行函数*/})}])
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{
"./bundle.js":(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);
const add = function (a, b) {\n return a + b\n}\n\n
__webpack_exports__[\"default\"] = (add);
\n\n//sourceURL=webpack:///./bundle/a.js?"
);
})
}]);
window["webpackJsonp"].push([]) = webpackJsonpCallback([])
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;
webpackJsonpCallback
function webpackJsonpCallback(data) {
var chunkIds = data[0];
var moreModules = data[1];
var moduleId, chunkId, i = 0, resolves = [];
for(;i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
// 返回resolve
resolves.push(installedChunks[chunkId][0]);
}
// 用于判断 加载成功
installedChunks[chunkId] = 0;
}
for(moduleId in moreModules) {
if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
// 当前模块内容 赋值给 modules['./bundle.js']
modules[moduleId] = moreModules[moduleId];
}
}
if(parentJsonpFunction) parentJsonpFunction(data);
// step3: 加载成功,执行resolve()
while(resolves.length) {
resolves.shift()();
}
};
3.3 分析
总结为5点
- import
__webpack_require__.e
函数 返回一个Promise.all
- 新增script标签 src 加载 import里面的文件
- 监听script.onerror和定时120s后,执行reject installedChunks[chunkId] = undefined
- 异步加载文件执行 resolve installedChunks[chunkId] = 1;
并把当前模块内容导入modules
变量上 - webpack_require.e().then里再执行__webpack_require__
4 import 动态加载-文件名为变量
4.1 基本用法
function loadModule(moduleName) {
return import(`./com/${moduleName}.js`)
.then(module => {
console.log('Module loaded:', module);
})
.catch(error => {
console.error('Error loading module:', error);
});
}
var input = document.getElementById('myInput');
input.addEventListener('input', function(event) {
const path = event.target.value
loadModule(path)
});
4.2 打包后核心代码
dist\mian.js
(() => {
// 定义启动处文件
var __webpack_modules__ = {
"./src/com lazy recursive ^\\.\\/.*\\.js$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
var map = {"./a.js": ["./src/com/a.js","src_com_a_js"],"./b.js": ["./src/com/b.js","src_com_b_js"],"./c.js": ["./src/com/c.js","src_com_c_js"]};
function webpackAsyncContext(req) {
var ids = map[req], id = ids[0];
return __webpack_require__.e(ids[1]).then(() => {
return __webpack_require__(id);
});
}
webpackAsyncContext.keys = () => (Object.keys(map));
webpackAsyncContext.id = "./src/com lazy recursive ^\\\\.\\\\/.*\\\\.js$";
module.exports = webpackAsyncContext;
},
"./src/index.js": (
__unused_webpack_module,
__unused_webpack_exports,
__webpack_require__
) => {
function loadModule(moduleName) {
return __webpack_require__("./src/com lazy recursive ^\\\\.\\\\/.*\\\\.js$\")(`./${moduleName}.js`)
.then(module => { console.log('Module loaded:', module); })
.catch(error => { console.error('Error loading module:', error);
})
}
var input = document.getElementById('myInput');
input.addEventListener('input', function(event) {
const path = event.target.value
loadModule(path)
});
}
};
// 重新封装require函数
function __webpack_require__(moduleId) {
var module = (__webpack_module_cache__[moduleId] = {
exports: {},
});
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
// 创建script标签加载异步js
__webpack_require__.l = (url, done, key, chunkId) => {
//...
script = document.createElement("script");
script.setAttribute("data-webpack", dataWebpackPrefix + key);
script.src = url;
script.onerror = onScriptComplete.bind(null, script.onerror);
script.onload = onScriptComplete.bind(null, script.onload);
needAttach && document.head.appendChild(script);
};
// 定义 chund 文件路径
__webpack_require__.u = (chunkId) => {return "" + chunkId + ".js";};
// 定义 publicPath
__webpack_require__.p = __webpack_require__.g.location //...
// 创建new Promise类型的jsonp 回调函数
var installedChunks = {main: 0};
__webpack_require__.e = (chunkId, promises) => {
var installedChunkData = installedChunks[chunkId]
if (installedChunkData !== 0) {
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
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);
__webpack_require__.l(url,loadingEnded,"chunk-" + chunkId,chunkId);
}
}
};
// 运行 JSONP callback
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) {
__webpack_modules__[moduleId] = moreModules[moduleId];
}
}
if (parentChunkLoadingFunction) parentChunkLoadingFunction(data);
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
installedChunks[chunkId][0]();
installedChunks[chunkId] = 0;
}
};
// 定义webpackChunkimport_path 函数,异步js会用到
var chunkLoadingGlobal = (self["webpackChunkimport_path"] = self["webpackChunkimport_path"] || []);
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(null,
chunkLoadingGlobal.push.bind(chunkLoadingGlobal)
);
//运行 启动处文件
var __webpack_exports__ = __webpack_require__("./src/index.js");
})();
dist\src_com_a_js.js
主要就是webpackChunkimport_path.push()
,也就是运行 webpackJsonpCallback
函数,将异步模块注册到__webpack_modules__
上
(self["webpackChunkimport_path"] = self["webpackChunkimport_path"] || []).push([
["src_com_a_js"],
{
"./src/com/a.js":
(__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, { "default": () => (__WEBPACK_DEFAULT_EXPORT__)});
const __WEBPACK_DEFAULT_EXPORT__ = ('1111');
}
])
4.3 分析
和import 文件名为常量相比,多了
./src/com lazy recursive ^\\.\\/.*\\.js$
,map对象封装动态模块下的所有代码文件、并返回webpackAsyncContext函数
用于加载制定的动态模块。
所以当 import($path)
对应路径为变量时,webpack会通过 require.context(dirname,useSubdirectories,RegExp)
的模式遍历获取当前目录下所有的文件,将之封装为map对象
(() => {
// 定义启动处文件
var __webpack_modules__ = {
"./src/com lazy recursive ^\\.\\/.*\\.js$": (
module,
__unused_webpack_exports,
__webpack_require__
) => {
var map = {"./a.js": ["./src/com/a.js","src_com_a_js"],"./b.js": ["./src/com/b.js","src_com_b_js"],"./c.js": ["./src/com/c.js","src_com_c_js"]};
function webpackAsyncContext(req) {
var ids = map[req], id = ids[0];
return __webpack_require__.e(ids[1]).then(() => {
return __webpack_require__(id);
});
}
webpackAsyncContext.keys = () => (Object.keys(map));
webpackAsyncContext.id = "./src/com lazy recursive ^\\\\.\\\\/.*\\\\.js$";
module.exports = webpackAsyncContext;
}
...
欢迎关注我的前端自检清单,我和你一起成长