一、运行环境打包
webpack 会假定项目的入口起点为 src/index.js ,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化。
我们要分析的就是 main.js 文件。
- main.js 文件中的代码实际上就是一个自执行函数,将一个对象参数传入其中。
- 对象参数的 key 为所有模块的路径代码, value 为该路径下的代码统一放入一个函数中。
- 自执行函数中,需要引入项目的入口文件 src/index.js 。
- 在自执行函数中定义一个 __webpack_require__ 函数,函数参数为自执行函数参数中的key 。
- 根据参数中的路径,找到对应的函数代码,并执行,传入对应参数 module 、 module.exports 、 __webpack_require__ 。
- 由于模块有缓存,所以需要定义一个变量 moduleExports ,将模块运行结果缓存起来。再次调用 __webpack_require__ 函数时判断是否存在缓存,如果已经加载过该模块,则直接返回缓存结果。
例: a.js
console.log("module a");
module.exports = "a";
index.js
console.log("index module");
var a = require("./a");
console.log(a);
my-main.js :手写一个 main.js
(function (modules) {
var moduleExports = {}; // 用于存放模块缓存
// require函数相当于是运行一个模块,得到模块导出结果
function __webpack_require__(moduleId) {
// 如果已经加载过该模块,直接返回缓存结果
if (moduleExports[moduleId]) {
return moduleExports[moduleId];
}
// moduleId 表示模块的路径
var func = modules[moduleId]; // 得到该模块路径对应的代码
var module = {
exports: {},
};
func(module, module.exports, __webpack_require__); // 执行该模块代码
var result = module.exports; // 得到模块导出的结果
moduleExports[moduleId] = result; // 将模块运行结果缓存起来
return result;
}
// 执行入口模块
__webpack_require__("./src/index.js"); // 执行入口模块,require函数相当于是运行一个模块,得到模块导出结果
})({
// 该对象保存了所有的模块,以及模块对应的代码
"./src/a.js": function (module, exports) {
console.log("module a");
module.exports = "a";
},
"./src/index.js": function (module, exports, __webpack_require__) {
console.log("index module");
var a = __webpack_require__("./src/a.js");
console.log(a);
},
});
需要注意的是:在 main.js 文件中,自执行函数的参数 value ,也就是路径所对应函数中的内容被 eval() 包裹起来,作用是为了在浏览器调试时更加方便,其中 "//# sourceURL=webpack://test/./src/index.js?" 代表了调试时候显示的路径。
二、生产环境打包
生产环境打包是将变量名简化减少打包体积,原理与运行环境打包一致!
生产环境中没有 eval() 函数。