webpack 产物分析

创建webpack-demo

使用webpack@5.89.0创建webpack-demo项目,文件结构如下:

utils.js

js 复制代码
export const printHello = () => {
  console.log("hello world");
};

const key = "yes";

export default key;

入口文件index.js

js 复制代码
import key, { printHello } from "./utils";

function component() {
  const element = document.createElement("div");
  element.innerText = "webpack";

  printHello();
  console.log("key: ", key);

  return element;
}

document.body.appendChild(component());

使用默认webpack配置,在package.json添加"build": "webpack --mode=development --devtool=false",使用开发模式并关闭devtool保证产物可读性

运行npm run build/dist目录下生成main.js文件

js 复制代码
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./src/utils.js":
/*!**********************!*\
  !*** ./src/utils.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__),
/* harmony export */   printHello: () => (/* binding */ printHello)
/* harmony export */ });
const printHello = () => {
  console.log("hello world");
};

const key = "yes";

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (key);


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* 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 });
/******/ 		};
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./src/utils.js");


function component() {
  const element = document.createElement("div");
  element.innerText = "webpack";

  (0,_utils__WEBPACK_IMPORTED_MODULE_0__.printHello)();
  console.log("key: ", _utils__WEBPACK_IMPORTED_MODULE_0__["default"]);

  return element;
}

document.body.appendChild(component());

})();

/******/ })()
;

对代码进行分析

  1. 代码最外层是一个立即执行函数
  2. 定义__webpack_modules__对象,key是模块路径,值是模块内容
  3. 定义__webpack_require__函数,并且添加.d,.r,.o三个属性,都是函数
  4. 执行入口代码

1. __webpack_modules__

__webpack_modules__是一个对象,key是模块文件相对根目录的路径./src/utils.js,值是一个函数,内部包含utils.js模块内容

js 复制代码
  var __webpack_modules__ = {
    "./src/utils.js": (
      __unused_webpack_module,
      __webpack_exports__,
      __webpack_require__
    ) => {
      // __unused_webpack_module 没有用到的参数
      // __webpack_exports__ 是一个空对象,把 utils.js 模块导出的内容添加到__webpack_exports__里
      __webpack_require__.r(__webpack_exports__);
      __webpack_require__.d(__webpack_exports__, {
        default: () => __WEBPACK_DEFAULT_EXPORT__,
        printHello: () => /* binding */ printHello,
      });
      
      // utils.js模块代码
      const printHello = () => {
        console.log("hello world");
      };

      const key = "yes";

      const __WEBPACK_DEFAULT_EXPORT__ = key;
    },
  };

2. __webpack_require__

__webpack_require__是一个函数,作用是返回module.exports对象

js 复制代码
function __webpack_require__(moduleId) { 
    // moduleId是模块的路径
    // 1. 从缓存对象__webpack_module_cache__取出模块信息,__webpack_module_cache__是一个空对象
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }

    // 2. 创建一个模块,并给__webpack_module_cache__赋值
    var module = (__webpack_module_cache__[moduleId] = {
      // no module.id needed
      // no module.loaded needed
      exports: {},
    });

    // 3. 从__webpack_modules__取出moduleId对应的函数并执行
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

    // 4. 返回module.exports
    return module.exports;
}

__webpack_require__.o定义了一个工具方法,作用是检查对象的属性是自身属性还是原型属性

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

__webpack_require__.r作用是给exports对象加一个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 });
};

__webpack_require__.d作用是把第二个参数的自身属性转移到exports对象上

js 复制代码
__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_modules__对象内容基本都是下面的形式

js 复制代码
{
    "path to file": (, exports, __webpack_require__) => {
        1. 执行 __webpack_require__.d 函数,把模块导出属性添加到 exports 对象上
        2. 模块内容
    }
}

3. 入口文件的执行

js 复制代码
  var __webpack_exports__ = {};
  // 使用立即执行函数,防止内部变量污染
  (() => {
  
    // 给__webpack_exports__添加esModule标志
    __webpack_require__.r(__webpack_exports__);
    
    // 取出源码中import模块导出的内容
    var _utils__WEBPACK_IMPORTED_MODULE_0__ =
      __webpack_require__("./src/utils.js");
      
    // 入口文件源码
    function component() {
      const element = document.createElement("div");
      element.innerText = "webpack";
      // 使用用模块导出的属性
      (0, _utils__WEBPACK_IMPORTED_MODULE_0__.printHello)();
      console.log("key: ", _utils__WEBPACK_IMPORTED_MODULE_0__["default"]);

      return element;
    }

    document.body.appendChild(component());
  })();

主要是两步工作:

  1. 取出模块导出的属性
  2. 执行入口文件源码,使用模块导出的属性

总结

  1. webpack的打包结果里,实现了一套基于webpack_requirewebpack_exports的一套模块化规范,通过把源码中的一个个文件作为key + function的形式存储进webpack_modules里,同时会把在源码中所有模块化规范 都改成webpak_require和他自己的exports,加载的时候就是执行对应的webpack_modules里的对应key的函数,这个函数执行完成以后就会把对应的exports对象里写满值,默认导出就会有一个default属性,在 expoprts对象上具名导出就是具体的keyexports对象上
  2. 由于导入语句使用了webpack_require,这个webpack_require会返回已经填充好值的exports对象
  3. 然后在使用的时候 直接是把我们原来使用模式从模块化改成对象.key的使用模式
相关推荐
小纯洁w1 小时前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
海盗强8 小时前
Webpack打包优化
前端·webpack·node.js
祈澈菇凉14 小时前
如何优化 Webpack 的构建速度?
前端·webpack·node.js
懒羊羊我小弟17 小时前
常用 Webpack Plugin 汇总
前端·webpack·npm·node.js·yarn
祈澈菇凉1 天前
Webpack的持久化缓存机制具体是如何实现的?
前端·webpack·gulp
懒羊羊我小弟3 天前
Webpack 基础入门
前端·webpack·rust·node.js·es6
刽子手发艺3 天前
Selenium+OpenCV处理滑块验证问题
opencv·selenium·webpack
懒羊羊我小弟3 天前
常用Webpack Loader汇总介绍
前端·webpack·node.js
真的很上进5 天前
【1.8w字深入解析】从依赖地狱到依赖天堂:pnpm 如何革新前端包管理?
java·前端·vue.js·python·webpack·node.js·reactjs
SuperherRo6 天前
信息收集-Web应用&JS架构&URL提取&数据匹配&Fuzz接口&WebPack分析&自动化
javascript·webpack·自动化·fuzz