webpack 构建阶段介绍

Webpack 的编译流程可以分解为 7 个核心阶段,结合 Webpack 5 源码(以 webpack/webpack 仓库主分支为准)的结构实现,这里为您进行详细阐述:


一、初始化阶段

入口文件lib/webpack.js

  1. 参数校验与配置合并
const createCompiler = options => {
  // 验证配置有效性
  const webpackOptionsValidationErrors = validateSchema(
    webpackOptionsSchema,
    options
  );
  // 合并默认配置(@webpack/configuration包)
  const compiler = new Compiler(options.context);
  compiler.options = new WebpackOptionsApply().process(options, compiler);
}
  1. 核心对象实例化
  • 创建 Compiler(顶层控制对象)实例(lib/Compiler.js
  • 创建 Compilation 工厂对象(lib/Compilation.js
  • 初始化内置插件系统(Tapable 继承实现)

关键源码示例

// 初始化Compiler
class Compiler {
  constructor(context) {
    this.hooks = Object.freeze({
      beforeRun: new AsyncSeriesHook(["compiler"]),
      run: new AsyncSeriesHook(["compiler"]),
      // ...超过30个生命周期钩子
    });
    // ...其他初始化
  }
}

二、模块解析(Resolve)阶段

核心类ResolverFactorylib/ResolverFactory.js

  1. 解析路径策略
  • 通过 EnhancedResolver 处理模块路径的复杂性(aliases、extensions 等)
  • 使用 enhanced-resolve 包实现多级缓存机制

关键代码流程

compiler.resolverFactory.hooks.resolver
  .for('normal')
  .tap('WebpackOptionsApply', () => {
    const resolver = createResolver({
      fileSystem: compiler.inputFileSystem,
      // ...包含39种解析配置参数
    });
    return resolver;
  });

三、模块加载(Loaders)阶段

核心处理器NormalModulelib/NormalModule.js

  1. Loader 执行链
runLoaders(
  resource: string,
  loaders: LoaderItem[],
  context: LoaderContext,
  callback: (err, result) => void
)
  1. 源码转换流程
  • 通过 LoaderRunnerlib/LoaderRunner.js)管道式处理
  • 支持 pitching loader 机制(反序执行 pitch 方法)

关键数据结构

class NormalModule extends Module {
  constructor({
    // ...,
    loaders,  // 最终生成的loader链
    resource
  }) {
    // ...初始化处理策略
  }
}

四、依赖图谱构建阶段

核心算法:深度优先遍历

  1. AST 分析
    惧使用 acorn 库生成 AST
const parser = new JavascriptParser();
parser.parse(source, {
  sourceType: module.buildInfo.moduleArgument ? "module" : "script",
  // ...携带15+种分析配置
});
  1. 依赖关系收集
  • 遍历 AST 时识别 import/require 等语法
  • 通过 ModuleGraph 类(lib/ModuleGraph.js)记录模块间依赖

五、Chunk 生成阶段

优化策略

  • 使用 SplitChunksPluginlib/optimize/SplitChunksPlugin.js)进行代码拆分
  • Entry dependencies 处理入口点依赖

关键代码

// 在Compilation实例中生成chunk
compilation.addEntry(context, entry, options, err => {
  // 内部调用_addModuleChain进行模块链式处理
});

六、代码生成阶段

核心方法codeGeneration()lib/Compilation.js

处理流程:

  1. 调用 Template.apply() 生成运行时代码
  2. 通过 MainTemplate(生产入口代码)和 ChunkTemplate(生成非入口代码)处理不同类型输出
  3. 使用 renderManifest 钩子允许插件参与渲染

源码结构示例

// 代码生成核心方法
compilation.codeGeneration({
  dependencyTemplates,
  runtimeTemplate,
  moduleTemplates,
  type: "runtime",
  // ...19种生成配置参数
});

七、输出阶段

核心类Compiler.writeAssets()(`lib/Compiler.js``

步骤明细:

  1. 调用 emitAssets() 触发插件钩子
  2. 通过 outputFileSystem(默认内存文件系统)写入磁盘
  3. 处理 hash 更新与缓存策略

关键输出逻辑

const emitFiles = err => {
  // ...校验输出目录等前置操作
  this.outputFileSystem.writeFile(targetPath, content, callback);
  // 触发assetEmitted等钩子
};

架构亮点分析

  1. 可插拔的 Hook 系统
  • 基于 Tapable 实现 200+ 生命周期钩子
  • 插件系统可在任意阶段注入逻辑
  1. 缓存体系
  • MemoryCachePlugin 实现内存级缓存
  • 支持持久化缓存(cache: { type: 'filesystem' }
  1. 多线程优化
  • 在 Parser 阶段使用 worker-farm 并行处理
  • 限制线程池大小防止资源耗尽

通过以上阶段分析可以看到,Webpack 通过模块化架构设计和精细的阶段划分,实现了高度可扩展的构建流程。理解各阶段的实现机制对优化构建性能和开发高级插件至关重要。

posted @   木燃不歇  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示