webpack打包文件分析

测试代码:

打包后的整体代码:

由上面可以看出,

  • 最外层是一个自执行匿名函数, 形成封闭的作用域
  • 一个__webpack_modules__对象
  • 一个__webpack_module_cache__对象
  • 一个__webpack_require__函数
  • 三个匿名自执行函数
  • 加载入口文件

webpack_modules

__webpack_modules__是一个文件路径作为key,函数作为value,对应文件内容放在函数体内。

js 复制代码
var __webpack_modules__ = {
  /**
   * 看到__webpack_require__函数,就知道这三个参数是啥了
   * @param {*} __unused_webpack_module  就是创建的新模块,不在缓存中
   * @param {*} __webpack_exports__ 新模块的exports对象或者缓存中exports对象
   * @param {*} __webpack_require__ 模块加载函数
   */

  "./src/index.js": (__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
    // 给当前的模块添加标识,这是一个ES Module
    __webpack_require__.r(__webpack_exports__);

    // 加载当前模块,具体做了啥操作,可以看__webpack_require__内部实现
    var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/utils.js");
    // 当走完上一步过后,_utils__WEBPACK_IMPORTED_MODULE_0__这个对象里面就包含了add方法了

    // 执行add方法
    const sum = (0, _utils__WEBPACK_IMPORTED_MODULE_0__.add)(1, 2);

    // 打印结果
    console.log(sum);
  },

  "./src/utils.js": (__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
    // 给当前的模块添加表示, 这是一个ES Module
    _webpack_require__.r(__webpack_exports__);

    // 下面介绍了,d方法是给新模块的exports添加属性的,属性的来源就是代码中最后export对象
    __webpack_require__.d(__webpack_exports__, { add: () => add });

    const add = (a, b) => {
      return a + b;
    };
  },
};

webpack_module_cache

被加载过模块的缓存集合

存放已经加载过的模块返回的值,可能一个模块被多个地方使用,每次加载前过来检查下,如果有,说明之前被加载过了,那么可以直接返回结果,避免模块重复加载。

webpack_require

用于加载和执行模块的核心函数,返回新模块的exports对象。

js 复制代码
function __webpack_require__(moduleId) {
  // 传入一个模块ID,其实就是模块对应的路径,例如: ./src/index.js

  // 判断当前这个模块是否在缓存中,如果在的话,直接从缓存中拿到返回值
  var cachedModule = __webpack_module_cache__[moduleId];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }

  // 创建一个新的模块,并且添加到缓存中,key为模块的路径,value为{ exports: {} }
  var module = (__webpack_module_cache__[moduleId] = {
    exports: {},
  });

  // 加载和执行模块中的代码
  __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

  // Return the exports of the module
  return module.exports;
}

三个自执行匿名函数

js 复制代码
/* webpack/runtime/define property getters */
(() => {
  // define getter functions for harmony exports
  __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/runtime/hasOwnProperty shorthand */
(() => {
  __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
})();

/* webpack/runtime/make namespace object */
(() => {
  // define __esModule on exports
  __webpack_require__.r = (exports) => {
    if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
    }
    Object.defineProperty(exports, "__esModule", { value: true });
  };
})();

给__webpack_require__函数对象添加d方法

给模块的exports属性中添加代码中export的属性

js 复制代码
/**
 * @param {*} exports __webpack_require__中返回的exports对象 {}
 * @param {*} definition 代码中最后export
 */
__webpack_require__.d = (exports, definition) => {
  // 遍历代码中export对象中所有的属性
  for (var key in definition) {
    // o方法是判断对象中是否存在某个属性
    // 代码中最后export包含这个属性,但是新的模块exports不包含,因此要给exports添加这个属性
    if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
      Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
    }
  }
};

解释:__webpack_require__不是创建出一个新的模块并返回对应的exports属性了吗,而开发时候文件会存在export {name: 'chenjiang', age: 26}导出代码,现在就是要把代码中导出的属性和属性值存放到新的模块exports中。

给__webpack_require__函数对象添加o方法

其实就是判断对象是否存在某个属性

javascript 复制代码
  __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);

给__webpack_require__函数对象添加r方法

给当前模块做个标识ES Module。

js 复制代码
  __webpack_require__.r = (exports) => {
    if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
    }
    Object.defineProperty(exports, "__esModule", { value: true });
  };

启动函数

直接加载执行入口文件

js 复制代码
var __webpack_exports__ = __webpack_require__("./src/index.js");

参考文档:

www.cnblogs.com/gaokai/p/15...

相关推荐
卸任5 分钟前
Vibe coding工具使用心得
前端·ai编程
brien91 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance1 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M1 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写1 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李1 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易1 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马2 小时前
前沿视界
前端
kyriewen2 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员
陈随易2 小时前
3000 万人在用的翻译插件,重写一遍就是一门生意
前端·后端·程序员