《二十三》Webpack 的模块化原理

Wepack 自身就已经支持模块化,开发者在代码中可以使用各种各样的模块化开发,使用 Webpack 进行打包后会对其进行转换,因此可以直接在浏览器中运行。其中最常见的 CommonJS 和 ESModule。

CommonJS 模块化实现原理:

  1. 新建 src/js/math/js 文件并编写代码。
    function sum(num1,  num2) {
        return num1 + num2
    }
    module.exports = {sum}
    
  2. 新建 src/index.js 文件并编写代码。
    const {sum} = require('./js/math.js')
    console.log(sum(10,20))
    
  3. webpack.config.js 配置文件中配置开发模式,使打包后生成的文件不进行压缩丑化,方便阅读代码。
    module.exports = {
        mode: 'development',
        devtool: 'source-map',
    }
    
  4. 运行 webpack 命令进行打包,查看生成的 dist/main.js 文件。
    1. Webpack 会定义一个对象用来存放模块的映射。以模块的路径作为 key,以一个函数中作为 value。
    2. Webpack 会定义一个对象作为加载模块的缓存。
    3. Webpack 会定义一个函数,当加载模块时都会通过这个函数来加载。
      - 判断缓存模块中是否已经加载过该模块,如果有的话,直接返回模块导出的方法。
      - 否则的话,会从存放模块映射的对象中获取出模块要导出的变量,将其放入模块缓存对象中,并导出。
    4. 有一个立即执行函数,真正开始执行代码逻辑,调用加载模块的函数来加载引入的模块。
    // 立即执行函数
    (() => {
        // 定义一个对象用来存放模块的映射。以模块的路径作为 key,以一个函数中作为 value。如果有多个模块,将会有多个 key/value
        var __webpack_modules__ = ({
            "./src/js/math.js": ((module) => {
                function sum(num1, num2) {
                    return num1 + num2
                }
                // 将要导出的变量放到 module.exorts 对象中
                module.exports = {sum}
            })
        });
    
        // 定义一个对象作为加载模块的缓存。如果模块被初次加载,就放到这个缓存对象中,之后再读取的话就会更加方便读取
        var __webpack_module_cache__ = {};
    
        // 定义一个函数,当加载模块时都会通过这个函数来加载
        function __webpack_require__(moduleId) {
            // 1. 判断缓存模块中是否已经有该模块,如果有的话,直接返回模块导出的方法,就不需要再去加载一次模块中的所有代码
            var cachedModule = __webpack_module_cache__[moduleId];
            if (cachedModule !== undefined) {
                return cachedModule.exports;
            }
            // 2. 如果一次都没有加载过该模块,给 module 变量和模块缓存对象 __webpack_module_cache__[moduleId] 赋值同一个对象
            var module = __webpack_module_cache__[moduleId] = {exports: {}};
            // 3. 通过执行模块映射对象中指定的模块来获取对应导出的变量
            __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
            // 4. 返回 module.exports
            return module.exports;
        }
    
        // 立即执行函数。真正开始执行代码逻辑
        (() => {
            // 加载 ./src/js/math.js 模块
            const {sum} = __webpack_require__("./src/js/math.js")
            console.log(sum(10, 20))
        })();
    
    })();
    

ESModule 模块化实现原理:

  1. 新建 src/js/math/js 文件并编写代码。
    export function sum(num1,  num2) {
        return num1 + num2
    }
    
  2. 新建 src/index.js 文件并编写代码。
    import {sum} from './js/math.js'
    console.log(sum(10,20))
    
  3. webpack.config.js 配置文件中配置开发模式,使打包后生成的文件不进行压缩丑化,方便阅读代码。
    module.exports = {
        mode: 'development',
        devtool: 'source-map',
    }
    
  4. 运行 webpack 命令进行打包,查看生成的 dist/main.js 文件。
    1. Webpack 会定义一个对象用来存放模块的映射。以模块的路径作为 key,以一个函数中作为 value。
    2. Webpack 会定义一个对象作为加载模块的缓存。
    3. Webpack 会定义一个函数,当加载模块时都会通过这个函数来加载。
      - 判断缓存模块中是否已经加载过该模块,如果有的话,直接返回模块导出的方法。
      - 否则的话,会从存放模块映射的对象中获取出模块要导出的变量,将其放入模块缓存对象中,并导出。
    4. 通过三个方法来为加载模块的方法置定义三个属性:添加一个属性来标识是一个 ESModule 模块;判断属性是定义在对象本身上的而不是继承自原型链;为属性设置一层代理,访问 exports 对象的属性其实是在访问 definition 中对应的 get。
    5. 有一个立即执行函数,真正开始执行代码逻辑,调用加载模块的函数来加载引入的模块。
    // 立即执行函数
    (() => {
        "use strict";
    
        // 定义一个对象用来存放模块的映射。以模块的路径作为 key,以一个函数中作为 value。如果有多个模块,将会有多个 key/value
        var __webpack_modules__ = ({
            "./src/js/math.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
                // 调用 r 的目的是为了记录该模块是一个 ESModule
                __webpack_require__.r(__webpack_exports__);
                // 调用 d 的目的是为了设置一层代理。访问 exports.sum 其实是在访问 definition 中的 get 方法
                __webpack_require__.d(__webpack_exports__, {
                    sum: () => (sum)
                });
                function sum(num1, num2) {
                    return num1 + num2
                }
            })
    
        });
    
        // 定义一个对象作为加载模块的缓存。如果模块被初次加载,就放到这个缓存对象中,之后再读取的话就会更加方便读取
        var __webpack_module_cache__ = {};
    
        // 定义一个函数,当加载模块时都会通过这个函数来加载
        function __webpack_require__(moduleId) {
            // 1. 判断缓存对象中是否有该模块,如果有的话,直接返回模块导出的方法,就不需要再去加载一次模块中的所有代码
            var cachedModule = __webpack_module_cache__[moduleId];
            if (cachedModule !== undefined) {
                return cachedModule.exports;
            }
            // 2. 如果一次都没有加载过该模块,给 module 变量和模块缓存对象 __webpack_module_cache__[moduleId] 赋值同一个对象
            var module = __webpack_module_cache__[moduleId] = {exports: {}};
            // 3. 通过执行模块映射对象中指定的模块来获取对应导出的变量
            __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
            // 4. 返回 module.exports
            return module.exports;
        }
    
        // 立即执行函数。给 __webpack_require__ 函数添加了一个 d 属性,为属性设置一层代理,访问 exports 对象的属性其实是在访问 definition 中对应的 get
        (() => {
            __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_require__ 函数添加了一个 o 属性,判断属性是定义在对象本身上的而不是继承自原型链
        (() => {
            __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
        })();
    
        // 立即执行函数。给 __webpack_require__ 函数添加了一个 r 属性,添加 __esModule = true 属性来标识是一个 ESModule 模块
        (() => {
            __webpack_require__.r = (exports) => {
                if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                    Object.defineProperty(exports, Symbol.toStringTag, {value: 'Module'});
                }
                Object.defineProperty(exports, '__esModule', {value: true});
            };
        })();
    
        // 立即执行函数。真正开始执行代码逻辑
    	var __webpack_exports__ = {};
        (() => {
            // 调用 r 的目的是为了记录该模块是一个 ESModule
            __webpack_require__.r(__webpack_exports__);
            // 加载 ./src/js/math.js 模块
            var _js_math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/math.js");
            console.log((0, _js_math_js__WEBPACK_IMPORTED_MODULE_0__.sum)(10, 20))
        })();
    
    })();
    

CommonJS 加载 ESModule 的原理:

在 Webpack 中,可以通过 CommonJS 导入通过 ESModule 导出的内容。

  1. 新建 src/js/math/js 文件并编写代码。
    export function sum(num1,  num2) {
      return num1 + num2
    }
    
  2. 新建 src/index.js 文件并编写代码。
    const {sum} = require('./js/math.js')
    console.log(sum(10,20))
    
  3. webpack.config.js 配置文件中配置开发模式,使打包后生成的文件不进行压缩丑化,方便阅读代码。
    module.exports = {
        mode: 'development',
        devtool: 'source-map',
    }
    
  4. 运行 webpack 命令进行打包,查看生成的 dist/main.js 文件。
    // 立即执行函数
    (function () {
       // 定义一个对象用来存放模块的映射。以模块的路径作为 key,以一个函数中作为 value。如果有多个模块,将会有多个 key/value
      var __webpack_modules__ = ({
        "./src/js/math.js": (function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
            "use strict";
             // 调用 r 的目的是为了记录该模块是一个 ESModule
            __webpack_require__.r(__webpack_exports__);
             // 调用 d 的目的是为了设置一层代理。访问 exports.sum 其实是在访问 definition 中的 get 方法
            __webpack_require__.d(__webpack_exports__, {
              sum: function () { return sum; }
            });
            function sum(num1, num2) {
              return num1 + num2
            }
          })
      });
    
       // 定义一个对象作为加载模块的缓存。如果模块被初次加载,就放到这个缓存对象中,之后再读取的话就会更加方便读取
      var __webpack_module_cache__ = {};
    
       // 定义一个函数,当加载模块时都会通过这个函数来加载
      function __webpack_require__(moduleId) {
         // 1. 判断缓存对象中是否有该模块,如果有的话,直接返回模块导出的方法,就不需要再去加载一次模块中的所有代码
        var cachedModule = __webpack_module_cache__[moduleId];
        if (cachedModule !== undefined) {
          return cachedModule.exports;
        }
          // 2. 如果一次都没有加载过该模块,给 module 变量和模块缓存对象 __webpack_module_cache__[moduleId] 赋值同一个对象
        var module = __webpack_module_cache__[moduleId] = {exports: {}};
         // 3. 通过执行模块映射对象中指定的模块来获取对应导出的变量
        __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
         // 4. 返回 module.exports
        return module.exports;
      }
    
        // 立即执行函数。给 __webpack_require__ 函数添加了一个 d 属性,为属性设置一层代理,访问 exports 对象的属性其实是在访问 definition 中对应的 get
      !function () {
        __webpack_require__.d = function (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_require__ 函数添加了一个 o 属性,判断属性是定义在对象本身上的而不是继承自原型链
      !function () {
        __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
      }();
    
        // 立即执行函数。给 __webpack_require__ 函数添加了一个 r 属性,添加 __esModule = true 属性来标识是一个 ESModule 模块
      !function () {
        __webpack_require__.r = function (exports) {
          if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
          }
          Object.defineProperty(exports, '__esModule', { value: true });
        };
      }();
    
      // 立即执行函数。真正开始执行代码逻辑
      !function () {
        // 加载 ./src/js/math.js 模块
        const math = __webpack_require__("./src/js/math.js")
        console.log(math.sum(10, 20))
      }();
    })();
    

ESModule 加载 CommonJS 的原理:

在 Webpack 中,可以通过 ESModule 导入通过 CommonJS 导出的内容。

  1. 新建 src/js/math/js 文件并编写代码。
    function sum(num1,  num2) {
      return num1 + num2
    }
    module.exports = {sum}
    
  2. 新建 src/index.js 文件并编写代码。
    import {sum} from './js/math.js'
    console.log(sum(10,20))
    
  3. webpack.config.js 配置文件中配置开发模式,使打包后生成的文件不进行压缩丑化,方便阅读代码。
    module.exports = {
        mode: 'development',
        devtool: 'source-map',
    }
    
  4. 运行 webpack 命令进行打包,查看生成的 dist/main.js 文件。
// 立即执行函数
(function () {
   // 定义一个对象用来存放模块的映射。以模块的路径作为 key,以一个函数中作为 value。如果有多个模块,将会有多个 key/value
  var __webpack_modules__ = ({
    "./src/js/math.js": (function (module) {
        function sum(num1, num2) {
          return num1 + num2
        }
        module.exports = { sum }
      })
  });

   // 定义一个对象作为加载模块的缓存。如果模块被初次加载,就放到这个缓存对象中,之后再读取的话就会更加方便读取
  var __webpack_module_cache__ = {};

   // 定义一个函数,当加载模块时都会通过这个函数来加载
  function __webpack_require__(moduleId) {
    // 1. 判断缓存对象中是否有该模块,如果有的话,直接返回模块导出的方法,就不需要再去加载一次模块中的所有代码
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    // 2. 如果一次都没有加载过该模块,给 module 变量和模块缓存对象 __webpack_module_cache__[moduleId] 赋值同一个对象
    var module = __webpack_module_cache__[moduleId] = {exports: {}};
    // 3. 通过执行模块映射对象中指定的模块来获取对应导出的变量
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    // 4. 返回 module.exports
    return module.exports;
  }

  // 立即执行函数。给 __webpack_require__ 函数添加了一个 n 属性,区分是 ESModule 还是 CommonJS 模块,通过不同的函数返回 module 并为函数设置一层代理
  !function () {
    __webpack_require__.n = function (module) {
      var getter = module && module.__esModule ?
        function () { return module['default']; } :
        function () { return module; };
      __webpack_require__.d(getter, { a: getter });
      return getter;
    };
  }();

  // 立即执行函数。给 __webpack_require__ 函数添加了一个 d 属性,为属性设置一层代理,访问 exports 对象的属性其实是在访问 definition 中对应的 get
  !function () {
    __webpack_require__.d = function (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_require__ 函数添加了一个 o 属性,判断属性是定义在对象本身上的而不是继承自原型链
  !function () {
    __webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
  }();

  // 立即执行函数。给 __webpack_require__ 函数添加了一个 r 属性,添加 __esModule = true 属性来标识是一个 ESModule 模块
  !function () {
    __webpack_require__.r = function (exports) {
      if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
      }
      Object.defineProperty(exports, '__esModule', { value: true });
    };
  }();

   // 立即执行函数。真正开始执行代码逻辑
  var __webpack_exports__ = {};
  !function () {
    "use strict";
     // 调用 r 的目的是为了记录该模块是一个 ESModule
    __webpack_require__.r(__webpack_exports__);
    // 加载 ./src/js/math.js 模块
    var _js_math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/js/math.js");
    var _js_math_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_js_math_js__WEBPACK_IMPORTED_MODULE_0__);
    console.log((0, _js_math_js__WEBPACK_IMPORTED_MODULE_0__.sum)(10, 20))
  }();
})();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值