Webpack打包原理:对比require与import的执行机制

作者:望舒

时间:2026-10-22

浏览:234

本文通过具体代码示例,深入分析Webpack打包后CommonJS的require与ES6模块的import在底层实现上的差异。文章解析了Webpack生成的立即执行函数如何包裹模块,以及__webpack_require__函数如何利用installedModules对象实现模块缓存与单例加载,揭示两者在打包后代码结构中的核心执行逻辑。

文章目录

Webpack打包原理:require与import执行机制深度解析

前言:

此前在使用ES6编写代码并通过Webpack打包上线时,运行一切正常。虽然曾瞥见过打包后的代码,因其结构复杂且混乱而未深入探究其内部运作机制,但确认加载后即可正常运行。

本文将通过一个具体案例,对比分析打包前与打包后代码的执行逻辑。

项目目录结构与文件配置

首先创建项目目录结构:新建一个根文件夹,内部包含app和public两个子文件夹。app文件夹用于存放原始数据及编写的JavaScript模块;public文件夹用于存放供浏览器读取的文件,包括Webpack打包生成的JS文件及index.html文件。接着创建以下三个文件:

  • index.html:存放于public文件夹中;
  • Greeter.js:存放于app文件夹中;
  • main.js:存放于app文件夹中;

此时的项目目录结构如下图所示:


项目结构

在index.html文件中编写基础HTML代码,其主要目的是引入打包后的JS文件(此处暂将打包后的文件名命名为bundle.js,后续将详细阐述其生成过程)。


<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>sample Project</title>
 </head>
 <body>
  <div id='root'>
  </div>
  <script src="bundle.js"></script>
 </body>
</html>

在Greeter.js中定义一个返回包含问候信息HTML元素的函数,并依据CommonJS规范将该函数导出为一个模块:


// Greeter.js
exports.greet= function() {
 var greet = document.createElement('div');
 greet.textContent = "Hi there and greetings!";
 return greet;
};
exports.USER_INFO = "userInfo";

在main.js文件中编写以下代码,用于将Greeter模块返回的节点插入页面:


//main.js 
 let {greeter,USER_INFO} =require('./Greeter.js');
console.log(USER_INFO);
document.querySelector("#root").appendChild(greeter());

CommonJS require打包后的执行逻辑

使用Webpack进行打包后,生成的代码结构如下:


(function(modules){     var installedModules = {};  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
  }
  __webpack_require__.m = modules;
  __webpack_require__.c = installedModules;
  __webpack_require__.d = function(exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        configurable: false,
        enumerable: true,
        get: getter
      });
    }
  };
  __webpack_require__.n = function(module) {
    var getter = module && module.__esModule ?
    function getDefault() {
      return module['default'];
    }:
    function getModuleExports() {
      return module;
    };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };
  __webpack_require__.o = function(object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  __webpack_require__.p = "";
  return __webpack_require__(__webpack_require__.s = 0);
})
(
[
(function(module, exports, __webpack_require__) {
  //main.js
  let {
    greeter,
    USER_INFO
  } = __webpack_require__(1);
  console.log(USER_INFO);
  document.querySelector("#root").appendChild(greeter());
}),
(function(module, exports) {
  // Greeter.js
  exports.greet = function() {
    var greet = document.createElement('div');
    greet.textContent = "Hi there and greetings!";
    return greet;
  };
  exports.USER_INFO = "userInfo";
})
]);

最外层是一个包裹着立即执行函数的结构(代码中加粗部分),其参数为一个数组,数组中的每一项对应一个模块,每个模块被包裹在(function(module, exports, __webpack_require__) {//模块内容 });定义的函数中。

立即执行函数执行时,会调用 return __webpack_require__(__webpack_require__.s = 0);指定的逻辑。

这表示执行传入数组中的第一个模块,即main.js。

运行后的每个模块会被挂载到installedModules = {}对象上。当后续代码需要该模块时,直接返回当前模块的缓存结果,不再重新执行代码块。

ES6 import打包后的实现差异

接下来,我们将require语法替换为import,观察打包后的实现方式有何不同。

我们将Greeter.js的内容修改如下:


// Greeter.js
export default function() {
 var greet = document.createElement('div');
 greet.textContent = "Hi there and greetings!";
 return greet;
};
export const USER_INFO = "userInfo";
main.js 文件中的代码,修改后
//main.js 
import greet,{USER_INFO} from './Greeter.js';
console.log(USER_INFO);
document.querySelector("#root").appendChild(greet());

再次执行打包操作:


(function(modules) {
  var installedModules = {};
  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports;
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    };
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    module.l = true;
    return module.exports;
  }
  __webpack_require__.m = modules;
  __webpack_require__.c = installedModules;
  __webpack_require__.d = function(exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        configurable: false,
        enumerable: true,
        get: getter
      });
    }
  };
  __webpack_require__.n = function(module) {
    var getter = module && module.__esModule ?
    function getDefault() {
      return module['default'];
    }: function getModuleExports() {
      return module;
    };
    __webpack_require__.d(getter, 'a', getter);
    return getter;
  };
  __webpack_require__.o = function(object, property) {
    return Object.prototype.hasOwnProperty.call(object, property);
  };
  __webpack_require__.p = "";
  return __webpack_require__(__webpack_require__.s = 0);
})([(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", {
    value: true
  });
  var __WEBPACK_IMPORTED_MODULE_0__Greeter_js__ = __webpack_require__(1);
  //main.js
  console.log(__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["a"]);
  document.querySelector("#root").appendChild(Object(__WEBPACK_IMPORTED_MODULE_0__Greeter_js__["b"])());
}),
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  __webpack_exports__["b"] = (function() {
    var greet = document.createElement('div');
    greet.textContent = "Hi there and greetings!";
    return greet;
  });;
  const USER_INFO = "userInfo";
  __webpack_exports__["a"] = USER_INFO;
})]);

总结

以上所述是小编给大家介绍的JavaScript中使用import 和require打包后实现原理分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章