Webpack打包原理:对比require与import的执行机制
本文通过具体代码示例,深入分析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打包后实现原理分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!
Docker 部署 Wiki.js 完整教程:从安装到初始化配置
2026-11-04 10:36:31
从其他平台迁移数据到 Wiki.js 的方法与注意事项
2026-11-03 23:50:05
Wiki.js 启动报错或数据库连接失败怎么排查解决
2026-11-03 09:49:52
macOS 使用 Homebrew 或 nvm 升级 Node.js 的最佳实践
2026-11-03 08:29:29
前端项目部署后如何提示用户版本更新详解
2026-11-01 02:17:48
