本文详细讲解基于Node.js的Webpack环境搭建与基础使用。内容涵盖从初始化npm、安装Webpack及html-webpack-plugin,到编写配置文件、执行打包命令并生成HTML的全过程。此外,深入解析了Webpack打包后的匿名函数结构、模块缓存机制及__webpack_require__的调用逻辑,帮助开发者理解模块加载与依赖管理的底层原理。
Webpack 环境搭建与项目初始化
学习新技术时难免感到困惑,尝试阅读博客教程往往难以顺利推进,最终仍需回归官方文档寻求答案。Webpack 的运行依赖于 Node.js 环境,因此第一步是确保已安装最新版本的 Node.js。
1. 项目初始化
安装 Node.js 后,新建一个项目目录(例如命名为 html5),并在命令行终端中切换至该目录。
npm init -y
执行初始化命令将生成默认的 package.json 文件,该文件包含项目的基础配置参数,可用于后续的初始安装。关于该文件的详细参数说明,可参考官方文档:https://docs.npmjs.com/files/package.json。
若不使用 -y 参数,命令行会交互式地提示设置各项参数,但通常直接接受默认值即可,无需额外配置。
2. 安装 Webpack
npm install webpack --save-dev
将 Webpack 安装到当前项目目录中。虽然可以通过 npm install webpack -g 将其全局安装,但这容易导致模块找不到等错误,因此推荐安装到当前项目的本地依赖中。
3. 目录结构设计
Webpack 是一款用于加载各种资源模块并进行打包的工具。因此,首先需要建立如下目录结构:

app 目录用于存放开发阶段的 JS 文件,包括组件和入口文件;build 目录用于存放打包后的输出文件;webpack.config.js 顾名思义用于配置 Webpack 的行为;package.json 则记录项目元数据。
component.js
export default function () {
var element = document.createElement('h1');
element.innerHTML = 'Hello world';
return element;
}
component.js 文件输出一个包含 h1 元素的组件。其中 export default 是 ES6 语法,表示指定默认导出,因此在 import 导入时无需使用大括号。
index.js
import component from './component';
document.body.appendChild(component());
index.js 的主要作用是引用 Component 模块,并在页面上渲染一个 h1 元素。但完成此功能还需要一个插件,因为目前尚未生成 index.html 文件。
npm install html-webpack-plugin --save-dev
html-webpack-plugin 插件用于自动生成 HTML 文件,需将其安装到开发依赖目录中。
Webpack 配置文件与打包流程
4. 配置 Webpack 文件
我们需要通过 webpack.config.js 文件告知 Webpack 如何启动构建。配置文件至少需要定义入口(entry)和输出(output)。对于多页面应用,则需要配置多个入口。此处使用了 Node.js 的 path 模块来处理路径。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PATHS = {
app: path.join(__dirname, 'app'),
build: path.join(__dirname, 'build'),
};
module.exports = {
entry: {
app: PATHS.app,
},
output: {
path: PATHS.build,
filename: '[name].js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack demo',
}),
],
};
初次查看此配置文件可能会感到困惑,主要涉及 exports 导出的三个部分:入口、输出和插件。入口指向 app 文件夹,默认会将包含 "index.js" 的文件作为入口。输出指定了 build 目录地址及文件名;[name] 是占位符,可视为 Webpack 提供的一个变量,具体用法后续会涉及。HtmlWebpackPlugin 插件负责生成默认的 HTML 文件。
5. 执行打包
完成上述准备工作后,直接在命令行输入 webpack 命令即可运行打包过程。

控制台输出包含了 Hash(每次打包值均不同)、Version、Time(耗时)以及输出的文件信息。此时打开 build 文件夹,会发现多出了 app.js 和 index.html 文件,双击打开 index.html 即可预览效果。

此外,也可以修改 package.json 文件以简化命令。
{
"name": "Html5",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"html-webpack-plugin": "^2.28.0",
"webpack": "^2.2.1"
}
}
在 scripts 中指定 build 脚本。在命令行中执行 npm run build 即可得到与直接运行 webpack 相同的结果。

页面显示 helloword。接下来查看生成的文件内容。
index.html 文件内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack demo</title>
</head>
<body>
<script type="text/javascript" src="app.js"></script></body>
</html>
该 HTML 文件默认引用了打包后的 app.js。
Webpack 打包源码与模块加载原理
6、源码解析
分析 app.js 文件。
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __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;
/******/ };
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony default export */ __webpack_exports__["a"] = function () {
var element = document.createElement('h1');
element.innerHTML = 'Hello world';
return element;
};
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__component__ = __webpack_require__(0);
document.body.appendChild(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__component__["a" /* default */])());
/***/ })
/******/ ]);
app.js 的内容相对复杂,整体结构是一个匿名函数。
(function(module) {
})([(function (){}), function() {}])
app 文件夹中的两个 JS 文件在打包后成为了该函数内的两个模块。函数执行开始时,通过 __webpack_require__ 进行初始化。
return __webpack_require__(__webpack_require__.s = 1);
此处指定从模块 1 开始执行(赋值语句的返回值即为该模块的值)。而模块 1 的调用是通过 __webpack_require__ 的特定语句执行的。
相关代码如下:
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
通过 call 方法调用模块的主要目的是传递参数。
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__component__ = __webpack_require__(0);
document.body.appendChild(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__component__["a" /* default */])());
/***/ })
__webpack_require__ 在加载每个模块时,会先检查模块缓存中是否存在;若不存在,则新建一个 module 对象:
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
在模块 1 中加载了模块 0,
var __WEBPACK_IMPORTED_MODULE_0__component__ = __webpack_require__(0);
__WEBPACK_IMPORTED_MODULE_0__component__ 返回的是模块 0 的 exports 部分。而之前 Component.js 中定义的默认方法被标记为
__webpack_exports__["a"] = function () {
var element = document.createElement('h1');
element.innerHTML = 'Hello world';
return element;
}
因此在模块 1 的定义中,通过 "a" 关键字来获取该方法:
相关代码如下:
document.body.appendChild(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__component__["a" /* default */])());
至此流程完整,但这里使用了 __webpack_require__.i 将原始值返回。
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
对于 i 函数的具体作用尚不完全清楚,相关注释也未提供详细解释,如有高手了解,欢迎指点。
小结:
Webpack 通过一个立即执行的匿名函数将各个开发模块作为参数进行初始化,每个 JS 文件(module)对应一个编号,每个 JS 中 export 的方法或对象拥有各自指定的关键字。通过这种方式,Webpack 将所有模块和接口方法进行统一管理。随后优先加载最后一个模块(通常是引用其他模块的主模块),进而触发其他模块的加载,使整个 JS 应用运行起来。至此,基本了解了 Webpack 的功能和部分原理,虽然逻辑略显复杂,且初期可能未感受到巨大优势,但仍需继续探索。
demo:webpack_jb51.rar 建议使用最新的 Node.js 版本进行安装,否则打包后的结果可能出现错误。
参考:
https://survivejs.com/webpack/developing/getting-started/
https://webpack.js.org/
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。