通过分析webpack打包后的文件,理解webpack到底做了什么。
举个简单的例子:
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
// JavaScript 执行入口文件
entry: './src/main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
}
};
// src/add
export default function(a, b) {
return a + b
}
// src/main.js
import Add from './add'
console.log(Add, Add(1, 2))
上述打包后的bundle.js文件精简内容如下:
// modules是存放所有模块的数组,数组中每个元素存储{ 模块路径: 模块导出代码函数 }
(function (modules) {
// 模块缓存作用,已加载的模块可以不用再重新读取,提升性能
var installedModules = {};
// 关键函数,加载模块代码
// 形式有点像Node的CommonJS模块,但这里是可跑在浏览器上的es5代码
function __webpack_require__(moduleId) {
// 判断是否已经加载过,加载过不在重新读取
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 先创建一个空模块,塞入缓存中
var module = (installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {},
});
// 运行模块导出的代码函数,this设定为module.exports,即将模块内容挂载到module.exports上
// es5规范中最终导出的都是module.exports = xxx 语句,这里直接将module传入,那么module中的exports就包含了所有的导出内容
modules[moduleId].call(
module.exports,
module,
module.exports,
__webpack_require__
);
// 当前函数已经被加载过
module.l = true;
// 返回加载的模块,调用方直接调用即可
return module.exports;
}
return __webpack_require__((__webpack_require__.s = "./src/main.js"));
})({
// add模块 # sourceURL= xxx 为注释内容,直接忽略
"./src/add.js": function (module, exports) {
eval(
"function add(a, b) {\r\n return a + b;\r\n}\r\nmodule.exports = add;\r\n\n\n//# sourceURL=webpack:///./src/add.js?"
);
},
// 入口模块
"./src/main.js": function (module, exports, __webpack_require__) {
eval(
'let Add = __webpack_require__(/*! ./add */ "./src/add.js");\r\nconsole.log(Add, Add(1, 2));\r\n\n\n//# sourceURL=webpack:///./src/main.js?'
);
},
});