首页
/ Webpack 5 源码导读:模块打包器 webpack 的安装、核心概念与源码实现

Webpack 5 源码导读:模块打包器 webpack 的安装、核心概念与源码实现

2026-09-05 23:47:04作者:侯霆垣

本文以 webpack 仓库的 README 为主体,系统讲解 webpack 的定位、安装方式、插件与 Loader 两大扩展机制、代码分割与产物优化等核心能力,并结合 lib/ 目录下的源码实现(入口导出、配置默认值、缓存策略)印证文档中的每项描述,帮助读者既能快速上手使用,又能深入理解其内部工作原理。

一、webpack 是什么

README 开篇对项目的定位是一句话:"Webpack is a module bundler"(webpack 是一个模块打包器),其主要用途是把 JavaScript 文件打包后供浏览器使用,同时它还能够转换、打包或封装几乎任何资源或静态资产。仓库根目录的 package.json 中的描述与之呼应:"Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand."

当前仓库的版本为 5.110.3(见 package.json),主入口为 lib/index.js,CLI 可执行文件由 bin/webpack.js 提供(package.jsonmainbin 字段)。README 中的 TL;DR 部分概括了 webpack 的五项核心能力:

  1. 多模块格式打包:支持 ES Modules、CommonJS、AMD 三种模块格式,且允许它们在同一个应用中混合使用;
  2. 代码分割:可以生成单个 bundle,也可以生成多个在运行时按需异步加载的 chunk,从而减少首屏加载时间;
  3. 编译期依赖解析:依赖关系在编译阶段就被解析确定,从而缩减运行时代码体积;
  4. Loader 预处理:Loader 可以在编译期预处理文件,例如 TypeScript 转 JavaScript、模板字符串编译为函数、图片转 Base64 等;
  5. 高度模块化的插件系统:通过插件完成应用所需的其他一切工作。

这五点能力都能在仓库源码中找到对应的实现落点:lib/dependencies/ 目录下有 140 个依赖解析相关的文件(Harmony、CommonJS、AMD 各自的依赖类型),lib/optimize/SplitChunksPlugin.js 负责代码分割,lib/NormalModule.jslib/NormalModuleFactory.js 承载 Loader 管线,而插件系统的核心抽象散落在 lib/index.js 导出的各类 Plugin 中。

二、安装方式

README 给出了两种安装命令:

使用 npm:

npm install --save-dev webpack

使用 yarn:

yarn add webpack --dev

package.jsonengines 字段可以看到,webpack 要求 Node.js 版本 >=10.13.0。安装完成后,CLI 可通过 bin/webpack.js 直接调用;若以编程方式集成,则引用 lib/index.js 导出的 API。

编程 API 的最小使用形态是调用 webpack(options, callback)。从 lib/webpack.js 的源码可以看到其主流程:先通过 schemas/WebpackOptions.check.js 做快速 schema 校验(快速路径失败时再回退到完整的 validateSchema),然后经 config/normalization.js 归一化配置、config/defaults.js 填充默认值,最后由 WebpackOptionsApply 应用插件并创建 Compiler;如果传入的是数组配置,则委托给 MultiCompiler 并行处理多个编译器实例。

三、插件系统(Plugins)

README 强调:"Webpack has a rich plugin interface. Most of the features within webpack itself use this plugin interface. This makes webpack very flexible."——webpack 内部的大多数功能本身就构建在插件接口之上,这正是其灵活性的来源。

lib/index.js 的导出清单可以直观验证这一点。webpack 以命名空间组织暴露了几乎全部的内置插件:

  • 顶层BannerPluginCleanPluginDefinePluginIgnorePluginHotModuleReplacementPluginProgressPluginProvidePluginSourceMapDevToolPluginWatchIgnorePluginDllPluginManifestPluginEntryPlugin 等;
  • webpack.idsNamedModuleIdsPluginDeterministicChunkIdsPlugin 等八种模块/chunk ID 策略插件;
  • webpack.optimizeModuleConcatenationPlugin(即 scope hoisting)、SplitChunksPluginRuntimeChunkPluginSideEffectsFlagPluginMinChunkSizePluginRealContentHashPlugin 等;
  • webpack.container / webpack.sharing:Module Federation 相关的 ModuleFederationPluginContainerPluginConsumeSharedPluginProvideSharedPlugin
  • webpack.web / webpack.esm / webpack.node:不同 target 的 chunk loading 运行时模块;
  • webpack.css / webpack.html:实验性的原生 CSS/HTML 处理模块,README 中也明确说明"Webpack can generate HTML pages and extract CSS files itself, both experimental",其 syntax 导出标注了 @experimental 标记(见 lib/index.jscsshtml 命名空间的注释)。

所有插件都通过统一的 hook 机制(基于 tapable,见 package.json 依赖)挂接到编译流程中。README 还提到一个用于开发调试的工具:tapable-tracer,可实时追踪 tapable hook 的执行过程并收集结构化调用栈,帮助构建高级插件时观察生命周期。

仓库中所有内置插件均有对应的 TypeScript 声明文件位于 declarations/plugins 目录(如 declarations/plugins/BannerPlugin.d.tsdeclarations/plugins/ProgressPlugin.d.ts),其 JSON Schema 位于 schemas/plugins(39 组 .json/.js/.ts 三元组),为第三方插件作者提供了参照样板。

四、Loader:把"任意静态资源"纳入打包

README 对 Loader 的表述是:"Webpack enables the use of loaders to preprocess files. This allows you to bundle any static resource way beyond JavaScript." 并说明 Loader 有两种启用方式:

  • require() 语句中使用 loadername! 前缀;
  • 通过 webpack 配置中的正则(即 module.rules)自动匹配应用。

一个典型的规则配置可以参考仓库自带示例 examples/loader/webpack.config.js

"use strict";

/** @type {import("webpack").Configuration} */
const config = {
	// mode: "development" || "production",
	module: {
		rules: [
			{
				test: /\.css$/,
				loader: "css-loader"
			}
		]
	}
};

module.exports = config;

README 还区分了哪些模块类型不需要 Loader:JavaScript、JSON 和 asset modules 是原生支持的;CSS 与 HTML 目前为实验性内置支持(对应 lib/css/lib/html/ 目录);而预处理器和模板引擎(LESS、Sass、Pug、Handlebars、Markdown 等)依然走 Loader 生态。仓库的 devDependencies 中实际挂载了 css-loaderhtml-loader@webdiscus/pug-loadercoffee-loaderbabel-loader 等,用于运行 examples/ 下约 70 个可执行示例,覆盖 asset、code-splitting、typescript、wasm、worker、module-federation 等场景。

Loader 在 webpack 内部由 lib/NormalModule.jslib/NormalModuleFactory.js 驱动,Loader 上下文类型声明见 declarations/LoaderContext.d.ts。规则匹配、编译器的实现位于 lib/rulesRuleSetCompiler 等),并有对应单测 test/RuleSetCompiler.unittest.jstest/LoaderRunner.unittest.js 验证行为。

五、性能:异步 I/O 与多级缓存

README 在 Performance 一节写道:"Webpack uses async I/O and has multiple caching levels. This makes webpack fast and incredibly fast on incremental compilations."

这句描述在源码中可以得到印证。lib/config/defaults.js 中的 applyCacheDefaults 函数(约第 725 行起)展示了缓存的分级逻辑:当 cache 选项为真时,根据 cache.type 区分 memory(内存缓存,供 watch 增量编译使用)与 filesystem(文件系统缓存),并自动推导 cacheDirectory——普通项目默认为 <cwd>/.cache/webpack,Yarn PnP 项目则落在 .pnp/.cache/webpackF(cache, "cacheDirectory", ...) 分支)。文件级缓存策略的清理行为由 FileCacheStrategy 相关测试 test/PackFileCacheStrategyCleanup.test.jstest/Compiler-filesystem-caching.test.js 覆盖。

从源码结构看,缓存之上还叠加了模块级别的懒计算(如 lib/util/memoize.jslib/util/LazySet.js)与 I/O 层面的异步队列(lib/util/ 中的 AsyncQueueQueueSemaphore,对应 test/AsyncQueue.unittest.jstest/Queue.unittest.js),共同构成 README 所说的"multiple caching levels"。

六、模块格式支持:ES2015+、CommonJS 与 AMD

README 说明 webpack 开箱即用地支持 ES2015+、CommonJS 和 AMD 三种模块格式:"It performs clever static analysis on the AST of your code. It even has an evaluation engine to evaluate simple expressions."

这一能力的实现主体是 lib/javascript/JavascriptParser.js 及其配套的 AST 静态分析(依赖 acornes-module-lexer,见 package.json)。lib/dependencies/ 目录下按模块格式拆分为 HarmonyImportDependencyCommonJsRequireDependencyAMDDefineDependency 等约 140 个文件;lib/index.js 还导出了 webpack.javascript.JavascriptParser 供第三方直接扩展解析器。仓库自带示例 examples/harmonyexamples/commonjs 分别演示了 ES Module 与 CommonJS 的打包行为,examples/harmony-interop/ 则专门演示 CJS 与 ESM 互相 re-export 的互操作场景。

七、代码分割(Code Splitting)

README 的描述是:webpack 允许把代码库拆分为多个 chunk,chunk 在运行时异步加载,从而降低初始加载时间。

仓库中最典型的示例是 examples/code-splitting/example.js

var a = require("a");
var b = require("b");
require.ensure(["c"], function(require) {
    require("b").xyz();
    var d = require("d");
});

其中 require.ensure(["c"], ...) 声明了一个异步边界:c 及边界内引入的 d 会被切分到独立 chunk 中,在调用时通过运行时注入的 chunk loading 逻辑(浏览器端默认 jsonp,见 lib/web/JsonpChunkLoadingRuntimeModule.js)按需加载。该示例的 webpack.config.js 中设置 optimization.chunkIds: "named",目的是在不同构建模式下保持 chunk 文件名一致,便于对比输出。

更完整的分割能力由 webpack.optimize.SplitChunksPlugin 提供(lib/optimize/SplitChunksPlugin.js),支持 cacheGroups 等配置项;仓库示例覆盖了多种切分策略:examples/common-chunk-and-vendor-chunk(公共 chunk + 第三方 vendor)、examples/code-splitting-depend-on-advanced(chunk 间依赖)、examples/extra-async-chunk(额外异步 chunk)、examples/http2-aggressive-splitting(面向 HTTP/2 的激进切分)。

八、产物优化(Optimizations)

README 指出 webpack 可以通过"去重高频使用的模块、压缩(minify)、以及让你完全控制哪些代码初始加载、哪些运行时加载"来减小产物体积,并能通过 hash 让 chunk 更利于缓存。对应到仓库实现:

九、浏览器兼容性

README 的 Browser Compatibility 一节给出两条硬性约束:

  1. webpack 支持所有符合 ES5 规范的浏览器,不支持 IE8 及以下
  2. 使用 import()require.ensure() 进行动态加载时,浏览器必须提供 Promise 实现;如需兼容老浏览器,必须先加载 Promise polyfill。

第 2 条在运行时可以得到印证:动态 chunk 加载的运行时模块(如 lib/runtime/ 下的 loadChunk 相关实现)普遍以 Promise 链组织异步流程。仓库的 devDependencies 中包含 es6-promise-polyfill,正是为这类兼容性测试准备的。

十、从源码看整体结构

对初次接触 webpack 源码的读者,可以从以下路径建立全局认知(均以仓库根目录为准):

  • lib/index.js:公共 API 门面。所有导出均采用 getter + memoize 懒加载,主函数 webpack() 通过 lazyFunction(() => require("./webpack")) 延迟初始化,避免引入时的重型依赖开销;
  • lib/webpack.js:API 入口,串联 schema 校验 → 配置归一化 → 默认值填充 → WebpackOptionsApplyCompiler
  • lib/Compiler.js / lib/Compilation.js:编译生命周期与产物(Asset)管理核心;
  • lib/NormalModule.js:普通模块的 Loader 管线执行;
  • lib/runtime(38 个运行时模块):注入产物中的 chunk 加载、publicPath 解析等运行时代码;
  • lib/configdefaults.js 负责按 mode 填充全部默认值(mode: "production" 下默认开启 minimize、hash 文件名等),normalization.js 负责入口格式等归一化。

测试体系同样值得参考:test/ 目录包含约 1800 个 cases/ 用例(验证各类模块格式与 Loader 组合)、约 1 万个 configCases/ 用例(验证配置项行为)、以及 unittest / basictest / longtest 三层测试文件,package.json 的 scripts 提供了 test:unittest:basictest:integration 等分层执行入口。

十一、贡献与项目治理

README 的 Contributing 部分欢迎文档更新、拼写修正、单元测试、issue 分诊等多种形式的贡献,并约定自行开发的 Loader/插件遵循 x-loaderx-webpack-plugin 的命名规范。仓库内的 CONTRIBUTING.mdGOVERNANCE.md(项目治理与 TSC 结构)、WORKING_GROUP.md(Core Working Group 维护机制)以及 RELEASE_SCHEDULE.md(补丁版本随时发布、minor 版本每 4 周周四发布)共同构成了完整的协作规范。README 还特别感谢了 GWT、webmake、browserify、require.js 等对 webpack 思想形成有影响的前驱项目。

小结

webpack 以"模块打包器"为核心定位,通过插件 hook、Loader 规则、AST 静态分析与多级缓存四层机制,把 ES Module/CommonJS/AMD 混合代码与任意静态资源统一编译为可按需加载的 chunk。阅读 README.md 建立概念框架后,沿着 lib/index.jslib/webpack.jslib/Compiler.js 的主链路,再结合 examples/ 下可按 yarn build:examples 运行的完整示例,即可自上而下地把文档描述与源码实现逐一对应起来。

登录后查看全文
热门项目推荐
相关项目推荐