咱们在 webpack初体验 这篇文章中演示到,浏览器不反对 CommonJS ,在特定场景下才反对 Es Module ,而 webpack 能够将这些模块化的代码解析成浏览器可辨认的语法。

那么 webpack 到底是对模块化做了怎么的解决呢?一起来看看。

我的项目构造

demo├─ src      │   ├─ utils│   │  ├─ common_math.js│   │  └─ esmodule_format.js  │   ├─ common_index.js│   ├─ esmodule_index.js│   └─ index.js├─  index.html├─  package.json└─  webpack.config.js

别离以 CommonJS 和 ES Module 两种形式来进行导入导出

// common_math.js —— 应用 CommonJS 导出function add(a, b) {  return a + b;}function sub(a, b) {  return a - b;}module.exports = {  add,  sub,};// esmodule_format.js  —— 应用 ES Module 导出function timeFormat() {  return "2022-02-02";}export { timeFormat };

config文件配置

webpack.config.js 中 "mode" 默认为 "production",此时代码是通过压缩和美化的,为了更利于浏览,咱们将它设置为 "development"。

而 "development" 模式下 "devtool" 默认为 "eval",会给代码减少很多临时咱们不须要的内容,所以将 "devtool" 设置为 "source-map"(对于 "source-map",下一篇文章会具体介绍)

const path = require("path");module.exports = {  entry: "./src/common_index.js",  mode: "development",  devtool: "source-map",  output: {    filename: "./bundle.js",    path: path.resolve(__dirname, "./dist"),  },};

CommonJS

首先让 webpack 解析 CommonJS 语法,所以入口指定 common_index.js

// common_index.js —— 应用 CommonJS 导入const { add, sub } = require("./utils/common_math.js");console.log(add(20, 30));console.log(sub(20, 30));

执行 npm run build ,为了不便浏览,将生成的 将 bundle.js 中的正文、以及自执行函数的最外层全副删除。

对于 CommonJS 的解决,次要有两个对象和一个函数

  • __webpack_modules__ 对象,用于保留文件门路和文件内容的映射关系
  • __webpack_module_cache__ 对象,用于缓存已加载过的文件,key值为文件门路,value为导出的内容
  • __webpack_require__ 函数,用于加载文件,将导出内容增加到 module.exports 和 exports 对象中

具体webpack编译源码

var __webpack_modules__ = {  // 定义一个对象,对象中的 key 为文件门路,value 为函数,函数中包含文件内容  "./src/utils/common_math.js": (module) => {    function add(a, b) {      return a + b;    }    function sub(a, b) {      return a - b;    }    module.exports = {      add,      sub,    };  },};// 定义用于缓存已加载过文件的对象var __webpack_module_cache__ = {};function __webpack_require__(moduleId) {  // 从缓存对象中取以后moduleId的value  var cachedModule = __webpack_module_cache__[moduleId];  // 如果存在间接返回 exports 对象  if (cachedModule !== undefined) {    return cachedModule.exports;  }  // 如果不存在,在缓存对象中减少 key 为 moduleId,值为 { export: {} } 的数据  var module = (__webpack_module_cache__[moduleId] = {    exports: {},  });  // 通过 moduleId,执行保留在 __webpack_modules__ 的办法,并传入参数 module 对象,  // 执行办法后,会批改 module.exports 以及 exports 对象  __webpack_modules__[moduleId](module, module.exports, __webpack_require__);  // 以后返回的就是 { add, sub } 这个对象  return module.exports;}var __webpack_exports__ = {}; // 这里没有用到// 执行 common_math.jsconst { add, sub } = __webpack_require__("./src/utils/common_math.js");console.log(add(20, 30));console.log(sub(20, 30));

通过以上形式,webpack 将浏览器不可辨认的 CommonJS 代码编译成了浏览器可失常运行的语法

ES Module

将webpack.config.js 文件中的入口改成 esmodule_index.js。

// esmodule_index.js —— 应用 ES Module 导入import { timeFormat } from "./utils/esmodule_format.js";console.log(timeFormat());

再执行 npm run build,为了便于浏览,还是将 bundle.js 中的正文、以及自执行函数的最外层、严格模式的规定全副删除。

ES Module 的实现与 CommonJS 相同之处在于,它也有这两个对象和一个函数

  • __webpack_modules__ 对象,用于保留文件门路和文件内容的映射关系
  • __webpack_module_cache__ 对象,用于缓存已加载过的文件,key值为文件门路,value为导出的内容
  • __webpack_require__ 函数,用于加载文件,将导出内容增加到 module.exports 和 exports 对象中

但 ES Module 更为简单一些,还存在这三个函数

  • __webpack_require__.d 将传入的对象属性和值遍历到 exports 对象上
  • __webpack_require__.o 判断某属性是否存在于某对象中
  • __webpack_require__.r 给应用 ES Module 实现模块化的文件中 exports 对象里减少 __esModule 属性

具体webpack编译源码

var __webpack_modules__ = {  "./src/utils/esmodule_format.js": (    __unused_webpack_module,    __webpack_exports__,    __webpack_require__  ) => {    // 给exports减少__esModule属性    __webpack_require__.r(__webpack_exports__);    // 将 esmodule_format.js 中导出的函数 timeFormat 增加到 exports 对象中    __webpack_require__.d(__webpack_exports__, {      timeFormat: () => timeFormat,    });    function timeFormat() {      return "2022-02-02";    }  },};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;}__webpack_require__.d = (exports, definition) => {  // 遍历 esmodule_format.js 中导出的对象  for (var key in definition) {    // 如果以后exports对象中不存在该属性,则复制到exports对象中    if (      __webpack_require__.o(definition, key) &&      !__webpack_require__.o(exports, key)    ) {      // 定义exports对象,将函数作为get属性      Object.defineProperty(exports, key, {        enumerable: true,        get: definition[key],      });    }  }};// 判断 prop 属性是否存在于 obj 对象中__webpack_require__.o = (obj, prop) =>  Object.prototype.hasOwnProperty.call(obj, prop);  // 给 exports 对象减少 __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__ = {};// __webpack_exports__ 高低两行临时没有用到__webpack_require__.r(__webpack_exports__);// 应用函数将导出内容加载到 exports 和 module.exports 对象中var _utils_esmodule_format_js__WEBPACK_IMPORTED_MODULE_0__ =  __webpack_require__("./src/utils/esmodule_format.js");// 以下代码和函数间接调用成果统一console.log(  (0, _utils_esmodule_format_js__WEBPACK_IMPORTED_MODULE_0__.timeFormat)());

webpack 对于 ES Module 和 CommonJS 解决形式有些不同,CommonJS 中是间接将属性增加到 exports 对象中,而 ES Module 是通过 defineProperty 定义到该属性的 存取选择器 get 中

ES Module 和 CommonJS 混合应用

将webpack.config.js 文件中的入口改成 index.js。

// index.js//  ES Module 导出的内容 CommonJS 导入const { timeFormat } = require( "./utils/esmodule_format.js");// CommonJS 导出的内容 ES Module 导入import { add, sub } from ("./utils/common_math.js");console.log(timeFormat());console.log(add(20, 30));console.log(sub(20, 30));

再执行 npm run build,为了便于浏览,还是将 bundle.js 中的正文、以及自执行函数的最外层、严格模式的规定全副删除。

模块化互相援用的形式复用了 ES Module 和 CommonJS 都有的两个对象和一个函数

  • __webpack_modules__ 对象,用于保留文件门路和文件内容的映射关系
  • __webpack_module_cache__ 对象,用于缓存已加载过的文件,key值为文件门路,value为导出的内容
  • __webpack_require__ 函数,用于加载文件,将导出内容增加到 module.exports 和 exports 对象中

同时也保留了 ES Module 独有的这三个函数

  • __webpack_require__.d 将传入的对象属性和值遍历到 exports 对象上
  • __webpack_require__.o 判断某属性是否存在于某对象中
  • __webpack_require__.r 给应用 ES Module 实现模块化的文件中 exports 对象里减少 __esModule 属性

再减少了一个函数

  • __webpack_require__.n 定义遍历赋值为函数,并在该变量上增加a属性,值为函数
var __webpack_modules__ = {  "./src/utils/common_math.js": (module) => {    function add(a, b) {      return a + b;    }    function sub(a, b) {      return a - b;    }    module.exports = {      add,      sub,    };  },  "./src/utils/esmodule_format.js": (    __unused_webpack_module,    __webpack_exports__,    __webpack_require__  ) => {    "use strict";    __webpack_require__.r(__webpack_exports__);    __webpack_require__.d(__webpack_exports__, {      timeFormat: () => timeFormat,    });    function timeFormat() {      return "2022-02-02";    }  },};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;}__webpack_require__.n = (module) => {  var getter =    module && module.__esModule ? () => module["default"] : () => module;  __webpack_require__.d(getter, { a: getter });  return getter;};__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 = (obj, prop) =>  Object.prototype.hasOwnProperty.call(obj, prop);  __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__ = {};__webpack_require__.r(__webpack_exports__);var _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(  "./src/utils/common_math.js");// 临时没有用到var _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0___default =__webpack_require__.n(_utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__);const { timeFormat } = __webpack_require__("./src/utils/esmodule_format.js");console.log(timeFormat());console.log(  (0, _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__.add)(20, 30));console.log(  (0, _utils_common_math_js__WEBPACK_IMPORTED_MODULE_0__.sub)(20, 30));

ES Module 和 CommonJS 混合应用的形式是被反对的,webpack 的解决形式就是将两者独自解决合并在一起。

以上就是webpack编译模块化文件的源码的内容,更多无关webpack的内容能够参考我其它的博文,继续更新中~