Webpack 入门实战:从零搭建项目与源码解析

作者:接口漫谈

时间:2026-10-22

浏览:481

本文详细讲解基于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 是一款用于加载各种资源模块并进行打包的工具。因此,首先需要建立如下目录结构:

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 命令即可运行打包过程。

Webpack 环境搭建与项目初始化

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

Webpack 环境搭建与项目初始化

此外,也可以修改 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 相同的结果。

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/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章