分析webpack编译结果, 实现__webpack_require__函数
本篇文章我们通过手写来分析一下Webpack打包后的代码, 并研究一下Webpack是如何将多个模块合并在一起的
首先控制台输入npm init -y
初始化一个项目, 再输入npm i webpack webpack-cli -D
安装Webpack
在src目录想创建入口文件index.js, index.js导入了一个文件a.js并打印
我使用的Webpack版本如下:
- webpack: 5.89.0
- webpack-cli: 5.1.4
// src/index.js
console.log('index module')
var a = require("./a")
console.log(a)
src目录下创建一个a.js文件, a.js文件中导出一个对象
// src/a.js
console.log("a module")
module.exports = {
a: "a",
b: "b"
}
首先我们定义一个对象__webpack_modules__
, 这个对象中保存着所有模块, 其中key是模块id, 一般是文件的路径作为key, value则是一个函数, 对应着模块的代码; 模块中运用到的module、exports、require作为函数的参数传入, require为了和node中的require做区别, 因此命名为__webpack_require__
// 该对象中保存着所有模块 以及模块对应代码
var __webpack_modules__ = {
"./src/a.js": function (module, exports) {
console.log("a module")
module.exports = {
a: "a",
b: "b"
}
},
"./src/index.js": function (module, exports, __webpack_require__) {
console.log("index module")
var a = __webpack_require__("./src/a.js")
console.log(a)
}
为了避免上面代码造成全局变量污染, 我们将它作为立即执行函数的参数传入
(function (__webpack_modules__) {})({
// 该对象中保存着所有模块 以及模块对应代码
"./src/a.js": function (module, exports) {
console.log("a module")
module.exports = {
a: "a",
b: "b"
}
},
"./src/index.js": function (module, exports, __webpack_require__) {
console.log("index module")
var a = __webpack_require__("./src/a.js")
console.log(a)
}
})
接下来实现__webpack_require__
函数, 该函数接收一个moduleId(__webpack_modules__
中的key)作为参数, 并运行一个模块, 得到该模块的导出结果
实现思路, 定义一个module对象, 用于存放导出结果, module对象中有一个exports属性, 该属性也对应一个对象; 通过moduleId我们可以得到该模块的函数, 执行这个函数就可以得到这个模块的导出结果; 在立即执行函数中调用__webpack_require__
执行入口文件
(function (__webpack_modules__) {
function __webpack_require__(moduleId) {
var module = {
exports: {}
}
var func = __webpack_modules__[moduleId] // 得到对应模块
func(module, module.exports, __webpack_require__) // 执行对应的模块代码
return module.exports // 返回导出结果
}
// 执行入口文件
__webpack_require__("./src/index.js")
})({
// 该对象中保存着所有模块 以及模块对应代码
"./src/a.js": function (module, exports) {
console.log("a module")
module.exports = {
a: "a",
b: "b"
}
},
"./src/index.js": function (module, exports, __webpack_require__) {
console.log("index module")
var a = __webpack_require__("./src/a.js")
console.log(a)
}
})
这样我们就是实现了Webpack合并多个模块, 但是当我们读取某一模块的时候, 我们应将它的导出结果缓存起来, 当下一次再导入该模块时, 就不需要再执行这个模块的代码, 直接从缓存中读取导出结果即可, 所以我们需要实现缓存完善上面代码
实现思路: 立即执行函数中定义一个__webpack_module_cache__
对象用于缓存, 在__webpack_require__
函数中, 优先从缓存中读取, 若缓存中有值, 那么直接返回缓存中的结果, 若没有值在执行对应模块的代码, 并将导出结果缓存, 再对之前的代码进行简单优化, 示例代码如下:
(function (__webpack_modules__) {
var __webpack_module_cache__ = {} // 用于缓存
function __webpack_require__(moduleId) {
// 从缓存中读取
var cacheModule = __webpack_module_cache__[moduleId]
// 缓存中有值 直接返回缓存的结果
if (cacheModule !== undefined) return cacheModule
// 定义module存放导出结果 并赋值给缓存对象
var module = __webpack_module_cache__[moduleId] = {
exports: {}
}
// 执行对应的模块代码
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
// 返回导出结果
return module.exports
}
// 执行入口函数
__webpack_require__("./src/index.js")
__webpack_require__("./src/index.js")
__webpack_require__("./src/index.js")
__webpack_require__("./src/index.js")
})({
// 该对象中保存着所有模块 以及模块对应代码
"./src/a.js": function (module, exports) {
console.log("a module")
module.exports = {
a: "a",
b: "b"
}
},
"./src/index.js": function (module, exports, __webpack_require__) {
console.log("index module")
var a = __webpack_require__("./src/a.js")
console.log(a)
}
})
此时我们控制输入npx webpack --mode=development
使用开发环境对代码进行打包, 将多余的注释删除后, 得到的代码如下:
(() => {
var __webpack_modules__ = ({
"./src/a.js":
((module) => {
eval("console.log(\"a module\")\n\nmodule.exports = {\n a: \"a\",\n b: \"b\"\n}\n\n\n//# sourceURL=webpack://web/./src/a.js?");
}),
"./src/index.js":
((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
eval("console.log('index module')\nconst a = __webpack_require__(/*! ./a */ \"./src/a.js\")\nconsole.log(a)\n\n//# sourceURL=webpack://web/./src/index.js?");
})
});
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");
})();
可以看到和我们所实现的有一些小区别, 其中Webpack的__webpack_modules__
是定义在立即执行函数中的, 我们是作为立即执行函数的参数传入, 其实没有什么区别;
另外__webpack_modules__
中的模块函数是使用eval执行的, 这是并且会加上一个注释, 其实这是为了开发者能够在浏览器控制台调试, 由于我们运行的是打包后的代码, 那么报错指向的错误地址就会指向打包后的代码, 是不利于我们调式的; 而使用eval配合//# sourceURL=webpack://web/./src/a.js?
可以看做是一个简易版的source map, 仅仅是一种方便调试的手段而已
其他的就没有什么区别了, 生产环境无非就是对这些变量名进行了压缩丑化, 也是可以看懂的, 到这里我们已经分析了Webpack的编译结果, 并实现了Webpack合并模块的核心代码
标签:__,exports,require,module,js,webpack From: https://blog.csdn.net/m0_71485750/article/details/139456387