在邂逅webpack时就有关于提到webpack的模块化,这里前情回顾一下:
webpack默认支持各种模块化开发,比如ES Mouele、CommonJS、AMD等。(新版本的浏览器默认只支持ES Module,而旧版本是不支持所有模块化的。有了webpack我们就可以自行选择想要的模块化方案,无需担心浏览器支不支持。)
而且更牛的是,webpack还让我们的commonJs可以使用ES Module,EsModule也可以使用commonJS。它们是可以混着用的。那这些是怎么实现的呢?
这里我们将尝试看一下打包出来后的js源码,就可以最直观地感受到webpack是怎样实现模块化的了。
Webpack实现CommonJS原理
首先我们写一段使用了commonJS的语法的代码,然后使用webpack打包出来,就可以看到webpack实现commonJS模块化后转化的代码了。(webpack配置打包mode模式为development,默认的production模式打包会改变我们写的变量名,不利于阅读)
//使用CommonJSconst { dateFormat, priceFormat } = require('./js/format');console.log(dateFormat("abc"));console.log(priceFormat("abc"));
我们可以看到打包出来的源码,是存在很多模板注释以及其他一些杂七杂八的东西影响我们阅读。我们接下来将删掉这些东西,然后看一下我们整理好的代码。

整理好都的代码:
//每个依赖的模块都在这个对象里存储:{模块的路径(key): 函数(value)}var __webpack_modules__ = {//这里的key:value,其实就是我们require要导入的文件路径与我们写的代码"./src/js/format.js": function (module) {const dateFormat = (date) => {return "2022-5-7";};const priceFormat = (price) => {return "100.00";};// 将我们要导出的变量, 放入到module对象中的exports对象module.exports = {dateFormat,priceFormat,};},};// 定义一个对象, 作为加载模块的缓存var __webpack_module_cache__ = {};// 这个函数就是webpack为我们生成的,用来替代commonJs的requirefunction __webpack_require__(moduleId) {// 1.判断缓存中是否已经加载过if (__webpack_module_cache__[moduleId]) {return __webpack_module_cache__[moduleId].exports;}// 2.给新建的module变量和__webpack_module_cache__[moduleId]缓存对象,一起赋值了同一个对象var module = (__webpack_module_cache__[moduleId] = { exports: {} });// 3.在保存路径名与代码的对象里,根据路径名拿到我们需要用到的代码__webpack_modules__[moduleId](module, module.exports, __webpack_require__);// 4.我们要require的东西已经保存到了module.exports里,returnreturn module.exports;}// 立即执行函数:开始执行代码逻辑!!!//其实这里就是我们写的代码,但是涉及commonJs模块化的地方进行了处理!(function () {// 1.使用webpack生成的 __webpack_require__函数 替代commonJS的requireconst { dateFormat, priceFormat } = __webpack_require__("./src/js/format.js");console.log(dateFormat("abc"));console.log(priceFormat("abc"));})();
现在我们就清楚webpack是怎样实现commonJS的模块化了。就是去重写commonJS的导入与导出,写成浏览器看得懂的样子。
并且还贴心地附送了缓存功能,下次进行导入导出直接在缓存里拿到就可以了,无需重复逻辑。
Webpack实现ES Module原理
老样子,写一段使用了ES Module 的代码,然后打包看看打包出来的代码。
//使用ES Moduleimport { sum, mul } from "./js/math";console.log(mul(20, 30));console.log(sum(20, 30));
打包后的代码如下:
// 1.定义了一个对象, 对象里面放的是我们的模块映射//每个依赖的模块都在这个对象里存储:{模块的路径(key): 函数(value)}var __webpack_modules__ = {"./src/es_index.js": function (__unused_webpack_module,__webpack_exports__,__webpack_require__) {// 调用r的目的:记录下__webpack_exports__是一个ES Modue__webpack_require__.r(__webpack_exports__);var _js_math__WEBPACK_IMPORTED_MODULE_0__ =__webpack_require__("./src/js/math.js");console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.mul(20, 30));console.log(_js_math__WEBPACK_IMPORTED_MODULE_0__.sum(20, 30));},"./src/js/math.js": function (__unused_webpack_module,__webpack_exports__,__webpack_require__) {__webpack_require__.r(__webpack_exports__);// 调用了d函数: 给exports设置了一个代理definition(我们现在传入的第二个参数就是definition)// exports对象中本身是没有对应的函数,所以我们通过exports拿到对应函数,其实是到代理definition里拿到的__webpack_require__.d(__webpack_exports__, {sum: function () {return sum;},mul: function () {return mul;},});const sum = (num1, num2) => {return num1 + num2;};const mul = (num1, num2) => {return num1 * num2;};},};// 2.模块的缓存var __webpack_module_cache__ = {};// 3.require函数的实现(加载模块)function __webpack_require__(moduleId) {// 1.判断缓存中是否已经加载过if (__webpack_module_cache__[moduleId]) {return __webpack_module_cache__[moduleId].exports;}// 2.给新建的module变量和__webpack_module_cache__[moduleId]缓存对象,一起赋值了同一个对象var module = (__webpack_module_cache__[moduleId] = {exports: {},});// 3.在保存路径名与代码的对象里,根据路径名module拿到我们需要用到的代码__webpack_modules__[moduleId](module, module.exports, __webpack_require__);// 4.我们要require的东西已经保存到了module.exports里,returnreturn module.exports;}//写了三个立即执行函数,为我们的__webpack_require__添加三个属性,分别是d、o、r,它们分别是一个函数,有各自的作用!(function () {// __webpack_require__.d属性:exports没有definition的值时,就修改exports的个体,保证exports可以拿到definition的值__webpack_require__.d = function (exports, definition) {//遍历definitionfor (var key in definition) {//如果exports里没有我们的key,就去修改get,这样每次get都可以从definition上拿到我们要的keyif (__webpack_require__.o(definition, key) &&!__webpack_require__.o(exports, key)) {Object.defineProperty(exports, key, {enumerable: true,get: definition[key],});}}};})();!(function () {__webpack_require__.o = function (obj, prop) {return Object.prototype.hasOwnProperty.call(obj, prop);};})();//r的作用是用来记录当前使用的模块化是ES Module类型!(function () {__webpack_require__.r = function (exports) {//如果支持Symbol,就会加这个东西:记录下exports是一个Module模块(exports就是用来存放我们使用模块化ES Moduel导入的东西)if (typeof Symbol !== "undefined" && Symbol.toStringTag) {Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });}// 记录下exports是一个ES ModuleObject.defineProperty(exports, "__esModule", { value: true });};})();//入口进入__webpack_require__("./src/es_index.js");
