基于 Webpack 5.x 源码分析
2026-03-18 | 技术深度解读
第一部分:基础架构
第二部分:核心数据结构
第三部分:核心算法
第四部分:优化与实践
Webpack 是现代 JavaScript 应用程序的静态模块打包器,核心是将依赖图转换为优化后的 bundle。
核心概念
模块图的作用
┌─────────────────────────────────────────────┐
│ Webpack Compilation │
├─────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Resolver │→ │ Parser │→ │ Module │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ↓ ↓ ↓ │
│ ┌────────────────────────────────────────┐ │
│ │ ModuleGraph (模块图) │ │
│ │ • 模块依赖关系 │ │
│ │ • 导出/导入追踪 │ │
│ │ • 连接状态管理 │ │
│ └────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────┐ │
│ │ ChunkGraph (Chunk图) │ │
│ │ • Module → Chunk 映射 │ │
│ │ • ChunkGroup 管理 │ │
│ │ • 运行时需求追踪 │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
1. make 阶段 - 解析入口,创建模块依赖图
2. finishMake - 完成模块构建,收集依赖
3. buildChunkGraph - 构建 Chunk 图 ⭐核心
4. seal 阶段 - 优化、分割、生成代码
5. emit - 写入文件系统
// Compilation.js 中的核心流程
this.hooks.make.callAsync(compilation, err => {
// 1. 解析入口依赖
// 2. 递归构建模块图
// 3. 调用 buildChunkGraph
this.buildChunkGraph(err => {
// 4. seal 和 emit
});
});
为什么需要两套图结构?
ModuleGraph
ChunkGraph
// Webpack 5 引入双图设计
// ModuleGraph: 语义层面的依赖
// ChunkGraph: 产物层面的组织
class ModuleGraph {
constructor() {
// Dependency → Connection 映射
this._dependencyMap = new WeakMap();
// Module → ModuleGraphModule 映射
this._moduleMap = new Map();
// 元数据存储
this._metaMap = new WeakMap();
// 缓存系统
this._cache = undefined;
}
// 核心方法
setResolvedModule(originModule, dependency, module)
getConnection(dependency)
getIncomingConnections(module)
getOutgoingConnections(module)
}
class ChunkGraph {
constructor(moduleGraph, hashFunction) {
// Module → ChunkGraphModule 映射
this._modules = new WeakMap();
// Chunk → ChunkGraphChunk 映射
this._chunks = new WeakMap();
// 异步块 → ChunkGroup 映射
this._blockChunkGroups = new WeakMap();
// 关联 ModuleGraph
this.moduleGraph = moduleGraph;
}
// 核心方法
connectChunkAndModule(chunk, module)
getModuleChunks(module)
getChunkModules(chunk)
isModuleInChunk(module, chunk)
}
每个 Module 在 ModuleGraph 中的内部表示
class ModuleGraphModule {
constructor() {
// 入连接:谁引用了我
this.incomingConnections = new SortableSet();
// 出连接:我引用了谁
this.outgoingConnections = undefined;
// 发出者模块
this.issuer = undefined;
// 优化信息
this.optimizationBailout = [];
// 导出信息
this.exports = new ExportsInfo();
// 索引信息
this.preOrderIndex = null;
this.postOrderIndex = null;
this.depth = null;
}
}
每个 Module 在 ChunkGraph 中的内部表示
class ChunkGraphModule {
constructor() {
// 所属的 Chunks
this.chunks = new SortableSet();
// 作为入口的 Chunks
this.entryInChunks = undefined;
// 作为运行时的 Chunks
this.runtimeInChunks = undefined;
// 模块哈希(按运行时)
this.hashes = undefined;
// 模块 ID
this.id = null;
// 运行时需求
this.runtimeRequirements = undefined;
}
}
每个 Chunk 在 ChunkGraph 中的内部表示
class ChunkGraphChunk {
constructor() {
// 包含的模块
this.modules = new SortableSet();
// 入口模块映射
this.entryModules = new Map();
// 运行时模块
this.runtimeModules = new SortableSet();
// 需要完整哈希的模块
this.fullHashModules = undefined;
// 运行时需求
this.runtimeRequirements = undefined;
// 运行时需求树
this.runtimeRequirementsInTree = new Set();
}
}
模块间的连接关系,表示依赖的边
class ModuleGraphConnection {
constructor(
originModule, // 源模块
dependency, // 依赖对象
module, // 目标模块
explanation, // 说明
weak, // 弱依赖
condition // 条件函数
) {
this.originModule = originModule;
this.dependency = dependency;
this.module = module;
this.weak = weak;
this.condition = condition;
this.active = true;
}
// 获取连接状态
getActiveState(runtime) {
if (!this.active) return false;
if (this.condition) return this.condition(runtime);
return true;
}
}
管理模块的导出信息,支持 Tree Shaking
class ExportsInfo {
constructor() {
this.exports = [];
this.otherExportsInfo = new ExportInfo(null);
}
// 获取提供的导出
getProvidedExports() {
return this.otherExportsInfo.provided
? true
: this.exports.map(e => e.name);
}
// 获取使用的导出
getUsedExports(runtime) {
const used = new Set();
for (const exportInfo of this.exports) {
if (exportInfo.getUsed(runtime) !== 0) {
used.add(exportInfo.name);
}
}
return used;
}
}
所有模块类型的基类,继承自 DependenciesBlock
class Module extends DependenciesBlock {
constructor(type, context, layer) {
super();
this.type = type;
this.context = context;
this.debugId = debugId++;
// 构建信息
this.buildMeta = undefined;
this.buildInfo = undefined;
// 警告和错误
this._warnings = undefined;
this._errors = undefined;
}
// 核心方法(子类实现)
identifier() { /* 模块唯一标识 */ }
build(options, compilation, resolver, fs, callback) { /* 构建 */ }
codeGeneration(context) { /* 代码生成 */ }
}
最常见的模块类型,代表一个源文件
class NormalModule extends Module {
constructor({
type, request, userRequest,
rawRequest, loaders, resource,
matchResource, parser, generator
}) {
super(type, getContext(resource));
this.request = request;
this.userRequest = userRequest;
this.rawRequest = rawRequest;
this.loaders = loaders;
this.resource = resource;
this.parser = parser;
this.generator = generator;
}
build(options, compilation, resolver, fs, callback) {
// 1. 运行 loaders
// 2. 使用 parser 解析 AST
// 3. 提取依赖
// 4. 保存构建结果
}
}
| 类型 | 作用 | 示例 |
|---|---|---|
| HarmonyImportDependency | ES Module 导入 | import { a } from './a' |
| CommonJsRequireDependency | CommonJS 引用 | require('./b') |
| AMDRequireDependency | AMD 引用 | require(['c'], fn) |
| ImportDependency | 动态导入 | import('./d') |
| ContextDependency | 上下文引用 | require.context('./e') |
class Chunk {
constructor(name) {
this.id = null;
this.name = name;
this.ids = null;
this.debugId = debugId++;
// ChunkGroups
this._groups = new SortableSet();
// 名称提示
this.idNameHints = new SortableSet();
// 运行时
this.runtime = undefined;
// 文件名
this.filenameTemplate = undefined;
this._cssFilenameTemplate = undefined;
}
hasRuntime() { return this.runtime !== undefined; }
canBeInitial() { /* 是否可以是入口 Chunk */ }
}
Chunk 的分组,管理 Chunk 之间的父子关系
class ChunkGroup {
constructor(name) {
this.name = name;
this.chunks = [];
this.parents = [];
this.children = [];
this._blocks = new Set();
this.index = -1;
}
isInitial() {
return this.parents.length === 0;
}
addChild(child) {
this.children.push(child);
child.parents.push(this);
}
getEntrypointChunk() {
return this.chunks[0];
}
}
特殊的 ChunkGroup,代表应用入口
class Entrypoint extends ChunkGroup {
constructor(name, options) {
super(name);
this.options = options;
this.runtimeChunk = undefined;
}
// 获取入口 Chunk
getEntrypointChunk() {
return this.chunks[0];
}
// 设置运行时 Chunk
setRuntimeChunk(chunk) {
this.runtimeChunk = chunk;
}
// 添加异步入口点
addAsyncEntrypoint(entrypoint) {
this._asyncEntrypoints.push(entrypoint);
}
}
// buildChunkGraph.js - 核心入口函数
exports.default = compilation => {
const { moduleGraph, chunkGraph } = compilation;
// 1. 获取入口点及其模块
const inputEntrypointsAndModules = new Map();
for (const [name, entry] of compilation.entries) {
const entrypoint = compilation.entrypoints.get(name);
const modules = getModules(entry.dependencies);
inputEntrypointsAndModules.set(entrypoint, modules);
}
// 2. 访问所有模块,建立 Chunk 关系
visitModules(
logger, compilation,
inputEntrypointsAndModules,
chunkGroupInfoMap, blockConnections,
blocksWithNestedBlocks,
allCreatedChunkGroups, maskByChunk
);
// 3. 处理连接
// ...
};
深度优先遍历模块依赖图,建立 Chunk 结构
const visitModules = (
logger, compilation,
inputEntrypointsAndModules,
chunkGroupInfoMap, ...
) => {
// 队列驱动的遍历
const queue = [];
const actions = {
ADD_AND_ENTER_ENTRY_MODULE: 0,
ADD_AND_ENTER_MODULE: 1,
ENTER_MODULE: 2,
PROCESS_BLOCK: 3,
LEAVE_MODULE: 5
};
// 从入口开始
for (const [chunkGroup, modules] of inputEntrypointsAndModules) {
for (const module of modules) {
queue.push({ action: ADD_AND_ENTER_MODULE, module, chunkGroup });
}
}
// 处理队列
processQueue();
};
const processBlock = (block) => {
// 获取块的模块依赖
const blockModules = getBlockModules(block, runtime);
for (let i = 0; i < blockModules.length; i += 3) {
const refModule = blockModules[i];
const activeState = blockModules[i + 1];
// 跳过已存在的模块
if (chunkGraph.isModuleInChunk(refModule, chunk)) continue;
// 根据状态决定如何处理
if (activeState === true) {
// 活跃依赖,加入队列
queue.push({
action: ADD_AND_ENTER_MODULE,
module: refModule, chunk, chunkGroup
});
} else {
// 条件依赖,记录但暂不处理
skippedModuleConnections.add([refModule, connections]);
}
}
// 处理嵌套的异步块
for (const b of block.blocks) {
iteratorBlock(b);
}
};
从模块中提取所有块的依赖信息
const extractBlockModules = (
module, moduleGraph, runtime, blockModulesMap
) => {
// 遍历模块的所有出连接
for (const connection of moduleGraph.getOutgoingConnections(module)) {
const d = connection.dependency;
const m = connection.module;
// 跳过无效连接
if (!d || !m || connection.weak) continue;
// 找到依赖所在的块
const block = moduleGraph.getParentBlock(d);
const index = moduleGraph.getParentBlockIndex(d);
// 记录到映射中
const modules = blockModulesMap.get(block);
modules[index * 3] = m;
modules[index * 3 + 1] = connection.getActiveState(runtime);
modules[index * 3 + 2] = connection;
}
};
// ChunkGraph.js - 建立模块与 Chunk 的双向关联
connectChunkAndModule(chunk, module) {
const cgm = this._getChunkGraphModule(module);
const cgc = this._getChunkGraphChunk(chunk);
// Module → Chunk
cgm.chunks.add(chunk);
// Chunk → Module
cgc.modules.add(module);
}
// 断开关联
disconnectChunkAndModule(chunk, module) {
const cgm = this._getChunkGraphModule(module);
const cgc = this._getChunkGraphChunk(chunk);
cgc.modules.delete(module);
cgm.chunks.delete(chunk);
}
// ModuleGraph.js - 记录模块解析结果
setResolvedModule(originModule, dependency, module) {
// 创建连接
const connection = new ModuleGraphConnection(
originModule,
dependency,
module,
undefined,
dependency.weak,
dependency.getCondition(this)
);
// 添加到目标模块的入连接
const connections = this._getModuleGraphModule(module).incomingConnections;
connections.add(connection);
// 添加到源模块的出连接
if (originModule) {
const mgm = this._getModuleGraphModule(originModule);
mgm.outgoingConnections.add(connection);
}
}
获取所有引用当前模块的连接
// ModuleGraph.js
getIncomingConnections(module) {
const connections = this._getModuleGraphModule(module).incomingConnections;
return connections;
}
// 使用示例:判断模块是否被使用
function isModuleUsed(moduleGraph, module, runtime) {
for (const connection of moduleGraph.getIncomingConnections(module)) {
if (connection.isTargetActive(runtime)) {
return true;
}
}
return false;
}
// 按 OriginModule 分组获取
getIncomingConnectionsByOriginModule(module) {
const connections = this._getModuleGraphModule(module).incomingConnections;
return connections.getFromUnorderedCache(getConnectionsByOriginModule);
}
获取当前模块引用的所有连接
// ModuleGraph.js
getOutgoingConnections(module) {
const connections = this._getModuleGraphModule(module).outgoingConnections;
return connections === undefined ? EMPTY_SET : connections;
}
// 使用示例:获取所有依赖模块
function getDependencies(moduleGraph, module) {
const deps = new Set();
for (const connection of moduleGraph.getOutgoingConnections(module)) {
if (connection.module) {
deps.add(connection.module);
}
}
return deps;
}
// 按 Module 分组获取
getOutgoingConnectionsByModule(module) {
const connections = this._getModuleGraphModule(module).outgoingConnections;
return connections.getFromUnorderedCache(getConnectionsByModule);
}
追踪流程
关键数据流
源码 → AST → Dependency
↓
Resolver → 路径解析
↓
ModuleGraph.setResolvedModule
↓
ModuleGraphConnection
↓
ModuleGraphModule.incoming/outgoing
Webpack 5 的 Chunk 分割基于模块图分析
// 1. 入口 Chunk - 包含入口模块及其同步依赖
// 2. 异步 Chunk - 由 import() 创建
// 3. Runtime Chunk - 可选的运行时分离
// SplitChunksPlugin 的核心逻辑
function determineChunk(module, chunkGroup) {
// 检查模块大小
if (module.size() < minSize) return false;
// 检查共享次数
const chunkCount = getModuleChunks(module).size;
if (chunkCount < minChunks) return false;
// 创建新的 vendor chunk
const newChunk = compilation.addChunk('vendors');
chunkGraph.connectChunkAndModule(newChunk, module);
}
分割策略
实现机制
// import() 转换
import('./utils.js')
// → 编译为
__webpack_require__.e(/* chunk id */)
.then(__webpack_require__.bind(null, './utils.js'))
// AsyncDependenciesBlock
// → 新的 ChunkGroup
// → 新的 Chunk
// RuntimeChunk 配置
optimization: {
runtimeChunk: {
name: 'runtime'
}
}
// 效果:
// 1. 将 webpack runtime 代码提取到独立文件
// 2. 入口 Chunk 只包含业务代码
// 3. runtime 变化不影响业务 Chunk hash
// ChunkGraph 追踪运行时需求
addModuleRuntimeRequirements(module, runtime, items) {
const cgm = this._getChunkGraphModule(module);
cgm.runtimeRequirements.update(runtime, (existing) => {
for (const item of items) existing.add(item);
return existing;
});
}
基于 ModuleGraph 的 ExportsInfo 追踪导出使用
// 1. 标记导出使用情况
for (const exportInfo of exportsInfo.exports) {
const used = exportInfo.getUsed(runtime);
if (used === 0) {
// 未使用,可以删除
exportInfo.setUsed(0);
}
}
// 2. FlagDependencyExportsPlugin 分析导出
// 3. SideEffectsFlagPlugin 处理副作用标记
// 4. InnerGraphPlugin 分析模块内部依赖
// 5. 最终代码生成时移除死代码
// package.json 中的 sideEffects
{
"sideEffects": false // 所有未使用的导出都可删除
}
模块合并优化,减少闭包开销
// ModuleConcatenationPlugin 实现
// 将多个模块合并到一个作用域
// 原始代码
// moduleA.js
export const a = 1;
// moduleB.js
import { a } from './a';
export const b = a + 1;
// 优化后(合并到一个函数作用域)
var __webpack_modules__ = {
"./moduleB.js": function(module, exports) {
const a = 1; // 内联
const b = a + 1;
module.exports = { b };
}
};
// 条件:ES Module + 无副作用 + 静态分析可行
┌──────────────┐ ┌──────────────┐
│ ModuleGraph │──────│ ModuleGraph │
│ │ │ Module │
└──────────────┘ └──────────────┘
│ │
│ _moduleMap │ exports
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Module │ │ ExportsInfo │
│ │ │ │
└──────────────┘ └──────────────┘
│ │
│ outgoing │ exports[]
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ModuleGraph │ │ ExportInfo │
│ Connection │ │ │
└──────────────┘ └──────────────┘
│
│ module
▼
┌──────────────┐
│ Module │
│ (target) │
└──────────────┘
┌──────────────┐ ┌──────────────┐
│ ChunkGraph │──────│ ChunkGraph │
│ │ │ Module │
└──────────────┘ └──────────────┘
│ │
│ _chunks │ chunks
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ChunkGraph │◄─────│ Chunk │
│ Chunk │ │ │
└──────────────┘ └──────────────┘
│ │
│ modules │ _groups
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Module │ │ ChunkGroup │
│ │ │ │
└──────────────┘ └──────────────┘
│
│ extends
▼
┌──────────────┐
│ Entrypoint │
│ │
└──────────────┘
Entry Point
│
▼
┌────────────────┐
│ Entrypoint │
│ (ChunkGroup) │
└────────────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ ModuleA │ │ ModuleB │ │ ModuleC │
└─────────┘ └─────────┘ └─────────┘
│ │ │
├──────────────┤ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ ModuleD │ │ ModuleE │ │ async() │
└─────────┘ └─────────┘ └─────────┘
│
▼
┌─────────────┐
│ New Chunk │
│ Group │
└─────────────┘
┌─────────────────────────────────────────┐
│ 1. initialize │
│ 创建 Compilation │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 2. make (hooks.make) │
│ 解析入口,构建模块 │
│ → ModuleGraph 形成 │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 3. buildChunkGraph │
│ 遍历模块图,创建 Chunk │
│ → ChunkGraph 形成 │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 4. seal │
│ 优化、分割、生成代码 │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 5. emit │
│ 写入文件系统 │
└─────────────────────────────────────────┘
源码 (index.js)
│
▼
┌──────────────┐
│ Loader │ 转换源码
│ Pipeline │
└──────────────┘
│
▼
┌──────────────┐
│ Parser │ 生成 AST
│ (acorn) │
└──────────────┘
│
▼
┌──────────────┐
│ Dependency │ 提取依赖
│ Extraction │ import/require
└──────────────┘
│
▼
┌──────────────┐
│ Resolver │ 解析路径
│ (enhanced) │
└──────────────┘
│
▼
┌──────────────┐
│ Module │ 创建模块实例
│ Created │
└──────────────┘
│
▼
┌──────────────┐
│ ModuleGraph │ 记录连接
│.setResolved │
└──────────────┘
buildChunkGraph()
│
▼
┌──────────────┐
│ visitModules│ 遍历入口
└──────────────┘
│
▼
┌──────────────┐
│ processQueue │ 处理模块队列
└──────────────┘
│
├─ ADD_AND_ENTER_MODULE
│ │
│ ▼
│ connectChunkAndModule()
│
├─ PROCESS_BLOCK
│ │
│ ▼
│ 处理依赖,加入队列
│
└─ iteratorBlock (异步)
│
▼
创建新 ChunkGroup
│
▼
创建新 Chunk
模块图优化
Chunk 图优化
// BigInt 掩码优化
const mask = ZERO_BIGINT;
for (const m of modules) {
mask |= ONE_BIGINT << BigInt(getModuleOrdinal(m));
}
// 快速判断模块是否在 Chunk 中
Webpack 5 的持久化缓存系统
// filesystem cache 配置
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
// ModuleGraph 序列化
class ModuleGraph {
freeze(cacheStage) {
this._cache = new WeakTupleMap();
this._cacheStage = cacheStage;
}
cached(fn, ...args) {
if (this._cache === undefined) return fn(this, ...args);
return this._cache.provide(fn, ...args, () => fn(this, ...args));
}
}
模块级缓存
图结构复用
// ModuleGraph 增量更新
updateModule(dependency, module) {
const connection = this.getConnection(dependency);
if (connection.module === module) return;
// 克隆连接,更新目标
const newConnection = connection.clone();
newConnection.module = module;
this._dependencyMap.set(dependency, newConnection);
}
配置优化
代码优化
常见问题
调试工具
// 获取模块图信息
const modules = compilation.modules;
const moduleGraph = compilation.moduleGraph;
for (const module of modules) {
const connections = moduleGraph.getIncomingConnections(module);
console.log(`${module.identifier()}: ${connections.size} refs`);
}
| 特性 | Webpack | Rollup | Vite |
|---|---|---|---|
| 模块图 | 双图设计 | 单图 | 基于 Rollup |
| Chunk 分割 | 灵活强大 | 简单 | Rollup |
| HMR | 内置 | 无 | 原生支持 |
| Tree Shaking | 支持 | 原生优化 | Rollup |
| 配置复杂度 | 高 | 中 | 低 |
| 适用场景 | 复杂应用 | 库开发 | 现代框架 |
已知改进
模块图演进
// 优化大型项目的模块图
module.exports = {
optimization: {
// 分离运行时
runtimeChunk: 'single',
// 代码分割
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
},
// Tree Shaking
usedExports: true,
sideEffects: true
},
cache: {
type: 'filesystem'
}
};
核心要点回顾:
模块图核心
扩展阅读
48 页技术深度解析完成!
🔷 Webpack 模块图构建 - 理解打包器的核心