🔷 Webpack 模块图构建

深度解析 ModuleGraph 与 ChunkGraph 核心原理

基于 Webpack 5.x 源码分析
2026-03-18 | 技术深度解读

📑 目录

第一部分:基础架构

  • Webpack 简介
  • 模块图架构
  • 核心编译流程
  • 双图设计理念

第二部分:核心数据结构

  • ModuleGraph / ChunkGraph
  • Module / Dependency
  • Chunk / ChunkGroup

第三部分:核心算法

  • buildChunkGraph
  • visitModules 遍历
  • Chunk 分割算法
  • Tree Shaking 实现

第四部分:优化与实践

  • 性能优化策略
  • 缓存机制
  • 实战案例

🔷 Webpack 简介

Webpack 是现代 JavaScript 应用程序的静态模块打包器,核心是将依赖图转换为优化后的 bundle。

核心概念

  • Entry - 构建入口
  • Output - 输出配置
  • Loader - 模块转换
  • Plugin - 扩展功能
  • Mode - 环境模式

模块图的作用

  • 记录模块依赖关系
  • 追踪导出使用情况
  • 支持代码分割
  • 实现 Tree Shaking
  • 优化 Chunk 生成

🏗️ 模块图架构

┌─────────────────────────────────────────────┐
│              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

  • Module → Chunk 映射
  • ChunkGroup 层级
  • 运行时需求
  • Chunk 优化依据
  • 生成产物结构
// Webpack 5 引入双图设计
// ModuleGraph: 语义层面的依赖
// ChunkGraph: 产物层面的组织

📊 ModuleGraph 概述

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)
}

📊 ChunkGraph 概述

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)
}

📦 ModuleGraphModule

每个 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;
  }
}

📦 ChunkGraphModule

每个 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;
  }
}

📦 ChunkGraphChunk

每个 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();
  }
}

🔗 ModuleGraphConnection

模块间的连接关系,表示依赖的边

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;
  }
}

📤 ExportsInfo

管理模块的导出信息,支持 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;
  }
}

📦 Module 基类

所有模块类型的基类,继承自 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) { /* 代码生成 */ }
}

📄 NormalModule

最常见的模块类型,代表一个源文件

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. 保存构建结果
  }
}

🔗 Dependency 体系

类型 作用 示例
HarmonyImportDependency ES Module 导入 import { a } from './a'
CommonJsRequireDependency CommonJS 引用 require('./b')
AMDRequireDependency AMD 引用 require(['c'], fn)
ImportDependency 动态导入 import('./d')
ContextDependency 上下文引用 require.context('./e')

📦 Chunk 类

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 */ }
}

📦 ChunkGroup

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];
  }
}

🎯 Entrypoint

特殊的 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 入口

// 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. 处理连接
  // ...
};

🔄 visitModules 遍历

深度优先遍历模块依赖图,建立 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();
};

⚙️ processBlock 处理

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);
  }
};

📤 extractBlockModules

从模块中提取所有块的依赖信息

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;
  }
};

🔗 connectChunkAndModule

// 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);
}

📝 setResolvedModule

// 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);
  }
}

📥 getIncomingConnections

获取所有引用当前模块的连接

// 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);
}

📤 getOutgoingConnections

获取当前模块引用的所有连接

// 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);
}

🔍 模块依赖追踪

追踪流程

  1. Parser 解析源码
  2. 识别 import/require
  3. 创建 Dependency 对象
  4. Resolver 解析路径
  5. 创建 ModuleGraphConnection

关键数据流

源码 → AST → Dependency
  ↓
Resolver → 路径解析
  ↓
ModuleGraph.setResolvedModule
  ↓
ModuleGraphConnection
  ↓
ModuleGraphModule.incoming/outgoing

✂️ Chunk 分割算法

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);
}

📦 代码分割原理

分割策略

  • entry - 多入口分割
  • import() - 动态导入
  • SplitChunks - 公共模块
  • runtime - 运行时分离

实现机制

// import() 转换
import('./utils.js')

// → 编译为
__webpack_require__.e(/* chunk id */)
  .then(__webpack_require__.bind(null, './utils.js'))

// AsyncDependenciesBlock
// → 新的 ChunkGroup
// → 新的 Chunk

⚡ Runtime 优化

// 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;
  });
}

🌳 Tree Shaking 实现

基于 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  // 所有未使用的导出都可删除
}

🔗 Scope Hoisting

模块合并优化,减少闭包开销

// 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 + 无副作用 + 静态分析可行

📊 模块图 UML

┌──────────────┐      ┌──────────────┐
│  ModuleGraph │──────│ ModuleGraph  │
│              │      │   Module     │
└──────────────┘      └──────────────┘
       │                     │
       │ _moduleMap          │ exports
       ▼                     ▼
┌──────────────┐      ┌──────────────┐
│    Module    │      │ ExportsInfo  │
│              │      │              │
└──────────────┘      └──────────────┘
       │                     │
       │ outgoing            │ exports[]
       ▼                     ▼
┌──────────────┐      ┌──────────────┐
│ ModuleGraph  │      │  ExportInfo  │
│ Connection   │      │              │
└──────────────┘      └──────────────┘
       │
       │ module
       ▼
┌──────────────┐
│    Module    │
│   (target)   │
└──────────────┘

📊 Chunk 图 UML

┌──────────────┐      ┌──────────────┐
│  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  │
└──────────────┘

📊 Chunk 生成流程

buildChunkGraph()
      │
      ▼
┌──────────────┐
│  visitModules│  遍历入口
└──────────────┘
      │
      ▼
┌──────────────┐
│ processQueue │  处理模块队列
└──────────────┘
      │
      ├─ ADD_AND_ENTER_MODULE
      │       │
      │       ▼
      │   connectChunkAndModule()
      │
      ├─ PROCESS_BLOCK
      │       │
      │       ▼
      │   处理依赖,加入队列
      │
      └─ iteratorBlock (异步)
              │
              ▼
          创建新 ChunkGroup
              │
              ▼
          创建新 Chunk

⚡ 性能优化策略

模块图优化

  • WeakMap 存储结构
  • SortableSet 排序缓存
  • 延迟初始化
  • 缓存计算结果

Chunk 图优化

  • BigInt 模块掩码
  • 批量连接处理
  • 延迟队列合并
  • 最小可用模块追踪
// 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));
  }
}

🔄 增量编译

模块级缓存

  • ModuleMemCaches
  • 按模块存储构建结果
  • 依赖哈希校验
  • 失效时只重建必要模块

图结构复用

// 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);
}

✅ 模块图设计最佳实践

配置优化

  • 合理设置入口数量
  • 使用 SplitChunks 分割
  • 启用 runtime chunk
  • 配置 sideEffects
  • 使用 ES Module

代码优化

  • 避免循环依赖
  • 减少动态导入
  • 使用命名导出
  • 标记纯函数
  • 控制模块大小

🔍 常见问题与调试

常见问题

  • 循环依赖警告
  • Chunk 过大
  • Tree Shaking 失效
  • 缓存失效
  • HMR 不工作

调试工具

  • webpack-bundle-analyzer
  • --profile --json
  • stats.toJson()
  • compilation.getStats()
  • webpack-debug
// 获取模块图信息
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
配置复杂度
适用场景 复杂应用 库开发 现代框架

🚀 Webpack 6 展望

已知改进

  • 移除废弃 API
  • 更好的类型支持
  • 性能优化
  • 简化的配置
  • 更好的调试体验

模块图演进

  • 更高效的图算法
  • 改进的缓存策略
  • 更好的增量编译
  • Rust 重写部分?
  • 更智能的 Chunk 分割

💡 实战案例

// 优化大型项目的模块图
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'
  }
};

📚 总结与扩展阅读

核心要点回顾:

模块图核心

  • ModuleGraph 记录依赖
  • ChunkGraph 组织产物
  • Connection 表示边
  • buildChunkGraph 是关键

扩展阅读

  • Webpack GitHub
  • Webpack 源码分析系列
  • Tobias Koppers 博客
  • Webpack 设计文档

48 页技术深度解析完成!

🔷 Webpack 模块图构建 - 理解打包器的核心