Webpack 的 Compiler 与 Compilation

Webpack 的 Compiler 与 Compilation

在 Webpack 的构建过程中,Compiler 和 Compilation 是两个最核心的概念,它们分别代表了编译器实例和单次编译过程。

Compiler 概述

Compiler 是 Webpack 的主环境对象,它包含了所有的配置信息,包括 options、loaders、plugins 等。在 Webpack 启动时会实例化一个 Compiler 对象,该对象在整个 Webpack 生命周期中都存在,只会被创建一次。

Compiler 核心特性

// Compiler 类的基本结构
class Compiler extends Tapable {
  constructor(context) {
    super();
    this.hooks = {
      // 环境准备钩子
      environment: new SyncHook([]),
      afterEnvironment: new SyncHook([]),
      entryOption: new SyncBailHook(['context', 'entry']),
      afterPlugins: new SyncHook(['compiler']),
      afterResolvers: new SyncHook(['compiler']),
      
      // 编译生命周期钩子
      beforeRun: new AsyncSeriesHook(['compiler']),
      run: new AsyncSeriesHook(['compiler']),
      beforeCompile: new AsyncSeriesHook(['params']),
      compile: new SyncHook(['params']),
      thisCompilation: new SyncHook(['compilation', 'params']),
      compilation: new SyncHook(['compilation', 'params']),
      make: new AsyncParallelHook(['compilation']),
      afterCompile: new AsyncSeriesHook(['compilation']),
      
      // 输出阶段钩子
      shouldEmit: new SyncBailHook(['compilation']),
      emit: new AsyncSeriesHook(['compilation']),
      afterEmit: new AsyncSeriesHook(['compilation']),
      assetEmitted: new AsyncSeriesHook(['file', 'content']),
      
      // 完成和错误处理钩子
      done: new AsyncSeriesHook(['stats']),
      failed: new SyncHook(['error']),
      
      // 监听模式钩子
      watchRun: new AsyncSeriesHook(['compiler']),
      watchClose: new SyncHook([]),
      invalid: new SyncHook(['filename', 'changeTime'])
    };
    
    // 编译器状态
    this.context = context;
    this.running = false;
    this.watchMode = false;
    this.outputPath = '';
    this.outputFileSystem = null;
    this.inputFileSystem = null;
    
    // 配置信息
    this.options = {};
    this.resolverFactory = new ResolverFactory();
    this.fileTimestamps = new Map();
    this.contextTimestamps = new Map();
    
    // 编译记录
    this.records = {};
    this.removedFiles = new Set();
    this.modifiedFiles = new Set();
    
    // 插件和解析器
    this.resolvers = {
      normal: null,
      context: null,
      loader: null
    };
  }
  
  // 运行编译
  run(callback) {
    if (this.running) {
      return callback(new Error('Compiler is already running'));
    }
    
    this.running = true;
    
    const finalCallback = (err, stats) => {
      this.running = false;
      if (callback) callback(err, stats);
    };
    
    // 执行编译前钩子
    this.hooks.beforeRun.callAsync(this, (err) => {
      if (err) return finalCallback(err);
      
      this.hooks.run.callAsync(this, (err) => {
        if (err) return finalCallback(err);
        
        // 开始编译
        this.compile((err, compilation) => {
          if (err) return finalCallback(err);
          
          // 输出文件
          this.emitAssets(compilation, (err) => {
            if (err) return finalCallback(err);
            
            // 编译完成
            const stats = new Stats(compilation);
            this.hooks.done.callAsync(stats, (err) => {
              if (err) return finalCallback(err);
              return finalCallback(null, stats);
            });
          });
        });
      });
    });
  }
  
  // 监听模式
  watch(watchOptions, handler) {
    if (this.running) {
      return handler(new Error('Compiler is already running'));
    }
    
    this.running = true;
    this.watchMode = true;
    
    return new Watching(this, watchOptions, handler);
  }
  
  // 创建编译实例
  compile(callback) {
    const params = this.newCompilationParams();
    
    this.hooks.beforeCompile.callAsync(params, (err) => {
      if (err) return callback(err);
      
      this.hooks.compile.call(params);
      
      const compilation = this.newCompilation(params);
      
      this.hooks.make.callAsync(compilation, (err) => {
        if (err) return callback(err);
        
        compilation.finish((err) => {
          if (err) return callback(err);
          
          compilation.seal((err) => {
            if (err) return callback(err);
            
            this.hooks.afterCompile.callAsync(compilation, (err) => {
              if (err) return callback(err);
              
              return callback(null, compilation);
            });
          });
        });
      });
    });
  }
  
  // 创建新的编译参数
  newCompilationParams() {
    const params = {
      normalModuleFactory: new NormalModuleFactory({
        context: this.options.context,
        fs: this.inputFileSystem,
        resolverFactory: this.resolverFactory
      }),
      contextModuleFactory: new ContextModuleFactory(this.resolverFactory)
    };
    return params;
  }
  
  // 创建新的编译实例
  newCompilation(params) {
    const compilation = new Compilation(this);
    compilation.fileTimestamps = this.fileTimestamps;
    compilation.contextTimestamps = this.contextTimestamps;
    compilation.name = this.name;
    compilation.records = this.records;
    compilation.compilationDependencies = params.compilationDependencies;
    
    this.hooks.thisCompilation.call(compilation, params);
    this.hooks.compilation.call(compilation, params);
    
    return compilation;
  }
  
  // 输出资源
  emitAssets(compilation, callback) {
    let outputPath;
    
    const emitFiles = (err) => {
      if (err) return callback(err);
      
      const assets = compilation.getAssets();
      
      // 异步输出所有资源
      asyncLib.forEachLimit(
        assets,
        15,
        (asset, callback) => {
          let targetFile = asset.name;
          let targetPath = this.outputPath;
          
          const queryStringIdx = targetFile.indexOf('?');
          if (queryStringIdx >= 0) {
            targetFile = targetFile.substr(0, queryStringIdx);
          }
          
          const writeOut = (err) => {
            if (err) return callback(err);
            
            const targetPath = this.outputPath + '/' + targetFile;
            const source = asset.source();
            
            // 触发资源输出钩子
            this.hooks.assetEmitted.callAsync(targetFile, source, callback);
          };
          
          if (asset.existsAt === targetPath) {
            return callback();
          }
          
          // 写入文件
          this.outputFileSystem.writeFile(
            this.outputPath + '/' + targetFile,
            asset.source(),
            writeOut
          );
        },
        (err) => {
          if (err) return callback(err);
          
          this.hooks.afterEmit.callAsync(compilation, callback);
        }
      );
    };
    
    this.hooks.emit.callAsync(compilation, emitFiles);
  }
}

Compiler 生命周期

单次构建
监听模式
Webpack启动
创建Compiler实例
加载配置和插件
初始化钩子系统
模式选择
run方法
watch方法
beforeRun钩子
watchRun钩子
run钩子
beforeCompile钩子
compile钩子
创建Compilation
thisCompilation钩子
compilation钩子
make钩子
编译完成
afterCompile钩子
shouldEmit钩子
emit钩子
输出文件
afterEmit钩子
done钩子
是否监听模式
等待文件变化
编译结束
文件变化检测
invalid钩子

Compilation 概述

Compilation 代表了一次单独的编译过程,包含了当前的模块资源、编译生成的资源、变化的文件等信息。每次文件变化都会创建一个新的 Compilation 实例。

Compilation 核心特性

// Compilation 类的基本结构
class Compilation extends Tapable {
  constructor(compiler) {
    super();
    this.compiler = compiler;
    this.hooks = {
      // 构建阶段钩子
      buildModule: new SyncHook(['module']),
      rebuildModule: new SyncHook(['module']),
      failedModule: new SyncHook(['module', 'error']),
      succeedModule: new SyncHook(['module']),
      finishModules: new AsyncSeriesHook(['modules']),
      finishRebuildingModule: new SyncHook(['module']),
      
      // 依赖处理钩子
      addEntry: new SyncHook(['entry', 'name']),
      failedEntry: new SyncHook(['entry', 'name', 'error']),
      succeedEntry: new SyncHook(['entry', 'name', 'module']),
      
      // 封装阶段钩子
      seal: new SyncHook([]),
      unseal: new SyncHook([]),
      beforeChunks: new SyncHook([]),
      afterChunks: new SyncHook(['chunks']),
      
      // 优化阶段钩子
      optimize: new SyncHook([]),
      optimizeModules: new SyncBailHook(['modules']),
      afterOptimizeModules: new SyncHook(['modules']),
      optimizeChunks: new SyncBailHook(['chunks', 'chunkGroups']),
      afterOptimizeChunks: new SyncHook(['chunks', 'chunkGroups']),
      optimizeTree: new AsyncSeriesHook(['chunks', 'modules']),
      afterOptimizeTree: new SyncHook(['chunks', 'modules']),
      
      // 模块和chunk ID优化
      beforeModuleIds: new SyncHook(['modules']),
      moduleIds: new SyncHook(['modules']),
      optimizeModuleIds: new SyncHook(['modules']),
      afterOptimizeModuleIds: new SyncHook(['modules']),
      beforeChunkIds: new SyncHook(['chunks']),
      optimizeChunkIds: new SyncHook(['chunks']),
      afterOptimizeChunkIds: new SyncHook(['chunks']),
      
      // 代码生成钩子
      beforeCodeGeneration: new SyncHook([]),
      afterCodeGeneration: new SyncHook([]),
      
      // 资源处理钩子
      beforeModuleAssets: new SyncHook([]),
      additionalChunkAssets: new SyncHook(['chunks']),
      additionalAssets: new AsyncSeriesHook([]),
      optimizeChunkAssets: new AsyncSeriesHook(['chunks']),
      afterOptimizeChunkAssets: new SyncHook(['chunks']),
      optimizeAssets: new AsyncSeriesHook(['assets']),
      afterOptimizeAssets: new SyncHook(['assets']),
      
      // 记录和哈希
      beforeHash: new SyncHook([]),
      afterHash: new SyncHook([]),
      recordHash: new SyncHook(['records']),
      record: new SyncHook(['compilation', 'records']),
      beforeModuleAssets: new SyncHook([]),
      additionalChunkAssets: new SyncHook(['chunks']),
      shouldGenerateChunkAssets: new SyncBailHook([]),
      beforeChunkAssets: new SyncHook([]),
      
      // 统计信息
      statsFactory: new SyncHook(['statsFactory', 'options']),
      statsPrinter: new SyncHook(['statsPrinter', 'options'])
    };
    
    // 编译状态
    this.name = undefined;
    this.needAdditionalPass = false;
    this.startTime = undefined;
    this.endTime = undefined;
    
    // 模块和依赖
    this.modules = new Set();
    this.chunks = new Set();
    this.chunkGroups = [];
    this.namedChunkGroups = new Map();
    this.namedChunks = new Map();
    this.moduleGraph = new ModuleGraph();
    this.chunkGraph = new ChunkGraph(this.moduleGraph);
    this.codeGenerationResults = new Map();
    
    // 入口点
    this.entries = new Map();
    this.entrypoints = new Map();
    this.asyncEntrypoints = [];
    
    // 资源
    this.assets = {};
    this.assetsInfo = new Map();
    
    // 缓存和文件系统
    this.cache = compiler.cache;
    this.fileSystemInfo = new FileSystemInfo(compiler.inputFileSystem);
    
    // 错误和警告
    this.errors = [];
    this.warnings = [];
    this.children = [];
    
    // 依赖关系
    this.fileDependencies = new Set();
    this.contextDependencies = new Set();
    this.missingDependencies = new Set();
    this.buildDependencies = new Set();
  }
  
  // 添加入口模块
  addEntry(context, entry, optionsOrName, callback) {
    const options = typeof optionsOrName === 'object' 
      ? optionsOrName 
      : { name: optionsOrName };
    
    this.hooks.addEntry.call(entry, options.name);
    
    this._addEntryItem(context, entry, 'main', options, (err, module) => {
      if (err) {
        this.hooks.failedEntry.call(entry, options.name, err);
        return callback(err);
      }
      
      this.hooks.succeedEntry.call(entry, options.name, module);
      return callback(null, module);
    });
  }
  
  // 构建模块
  buildModule(module, callback) {
    this.hooks.buildModule.call(module);
    
    module.build(
      this.options,
      this,
      this.resolverFactory.get('normal', module.resolveOptions),
      this.inputFileSystem,
      (err) => {
        if (err) {
          this.hooks.failedModule.call(module, err);
          return callback(err);
        }
        
        this.hooks.succeedModule.call(module);
        return callback();
      }
    );
  }
  
  // 处理模块依赖
  processModuleDependencies(module, callback) {
    const dependencies = new Map();
    
    // 收集所有依赖
    const addDependency = (dep) => {
      const resourceIdent = dep.getResourceIdentifier();
      if (resourceIdent) {
        const factory = this.dependencyFactories.get(dep.constructor);
        if (factory) {
          dependencies.set(resourceIdent, {
            factory,
            dependencies: dependencies.get(resourceIdent) || [],
            dependency: dep
          });
          dependencies.get(resourceIdent).dependencies.push(dep);
        }
      }
    };
    
    // 遍历模块的所有依赖
    for (const dependency of module.dependencies) {
      addDependency(dependency);
    }
    
    // 异步处理所有依赖
    asyncLib.forEach(
      Array.from(dependencies.values()),
      (item, callback) => {
        this._handleModuleDependency(module, item, callback);
      },
      callback
    );
  }
  
  // 封装编译结果
  seal(callback) {
    this.hooks.seal.call();
    
    // 完成模块构建
    this.hooks.finishModules.callAsync(this.modules, (err) => {
      if (err) return callback(err);
      
      // 创建chunk图
      this._createChunkGraph();
      
      // 优化
      this._optimize();
      
      // 代码生成
      this._codeGeneration((err) => {
        if (err) return callback(err);
        
        // 创建哈希
        this._createHash();
        
        // 生成资源
        this._createModuleAssets();
        this._createChunkAssets();
        
        callback();
      });
    });
  }
  
  // 创建chunk图
  _createChunkGraph() {
    this.hooks.beforeChunks.call();
    
    // 为每个入口创建chunk
    for (const [name, entrypoint] of this.entrypoints) {
      const chunk = new Chunk(name);
      chunk.entryModule = entrypoint.getRuntimeChunk();
      this.chunks.add(chunk);
      this.namedChunks.set(name, chunk);
    }
    
    this.hooks.afterChunks.call(this.chunks);
  }
  
  // 优化过程
  _optimize() {
    this.hooks.optimize.call();
    
    // 优化模块
    while (this.hooks.optimizeModules.call(this.modules)) {
      // 重复执行直到没有更多优化
    }
    this.hooks.afterOptimizeModules.call(this.modules);
    
    // 优化chunks
    while (this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups)) {
      // 重复执行直到没有更多优化
    }
    this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
    
    // 优化依赖树
    this.hooks.optimizeTree.callAsync(this.chunks, this.modules, (err) => {
      if (err) return;
      this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
    });
    
    // 优化模块ID
    this.hooks.beforeModuleIds.call(this.modules);
    this.hooks.moduleIds.call(this.modules);
    this.hooks.optimizeModuleIds.call(this.modules);
    this.hooks.afterOptimizeModuleIds.call(this.modules);
    
    // 优化chunk ID
    this.hooks.beforeChunkIds.call(this.chunks);
    this.hooks.optimizeChunkIds.call(this.chunks);
    this.hooks.afterOptimizeChunkIds.call(this.chunks);
  }
  
  // 代码生成
  _codeGeneration(callback) {
    this.hooks.beforeCodeGeneration.call();
    
    const jobs = [];
    for (const module of this.modules) {
      for (const runtime of this.chunkGraph.getModuleRuntimes(module)) {
        jobs.push({ module, runtime });
      }
    }
    
    asyncLib.eachLimit(
      jobs,
      100,
      ({ module, runtime }, callback) => {
        this._codeGenerationModule(module, runtime, callback);
      },
      (err) => {
        if (err) return callback(err);
        this.hooks.afterCodeGeneration.call();
        callback();
      }
    );
  }
  
  // 为单个模块生成代码
  _codeGenerationModule(module, runtime, callback) {
    const codeGenerationResult = module.codeGeneration({
      dependencyTemplates: this.dependencyTemplates,
      runtimeTemplate: this.runtimeTemplate,
      moduleGraph: this.moduleGraph,
      chunkGraph: this.chunkGraph,
      runtime: runtime
    });
    
    this.codeGenerationResults.set(module, codeGenerationResult);
    callback();
  }
  
  // 创建模块资源
  _createModuleAssets() {
    this.hooks.beforeModuleAssets.call();
    
    for (const module of this.modules) {
      if (module.buildInfo.assets) {
        for (const assetName of Object.keys(module.buildInfo.assets)) {
          const fileName = this.getPath(assetName, {
            module
          });
          
          this.assets[fileName] = module.buildInfo.assets[assetName];
          this.assetsInfo.set(fileName, {
            sourceFilename: module.resource
          });
        }
      }
    }
  }
  
  // 创建chunk资源
  _createChunkAssets() {
    for (const chunk of this.chunks) {
      const manifest = this.getRenderManifest({
        chunk,
        hash: this.hash,
        fullHash: this.fullHash,
        outputOptions: this.outputOptions,
        moduleTemplates: this.moduleTemplates,
        dependencyTemplates: this.dependencyTemplates
      });
      
      for (const fileManifest of manifest) {
        const fileName = fileManifest.pathOptions 
          ? this.getPath(fileManifest.filename, fileManifest.pathOptions)
          : fileManifest.filename;
          
        this.assets[fileName] = fileManifest.render();
        
        this.assetsInfo.set(fileName, {
          chunk,
          contenthash: fileManifest.hash
        });
        
        chunk.files.add(fileName);
      }
    }
  }
  
  // 创建哈希
  _createHash() {
    this.hooks.beforeHash.call();
    
    const hash = createHash(this.outputOptions.hashFunction);
    
    // 为每个chunk计算哈希
    for (const chunk of this.chunks) {
      const chunkHash = createHash(this.outputOptions.hashFunction);
      
      for (const module of this.chunkGraph.getOrderedChunkModules(chunk)) {
        chunkHash.update(module.hash);
      }
      
      chunk.hash = chunkHash.digest('hex');
      hash.update(chunk.hash);
    }
    
    this.hash = hash.digest('hex');
    this.fullHash = this.hash;
    
    this.hooks.afterHash.call();
  }
  
  // 获取统计信息
  getStats() {
    return new Stats(this);
  }
  
  // 获取所有资源
  getAssets() {
    const array = [];
    for (const assetName of Object.keys(this.assets)) {
      if (Object.prototype.hasOwnProperty.call(this.assets, assetName)) {
        array.push({
          name: assetName,
          source: this.assets[assetName],
          info: this.assetsInfo.get(assetName) || {}
        });
      }
    }
    return array;
  }
}

Compilation 生命周期

通过深入分析 Compilation 的构建过程,我们可以更好地理解 Webpack 的工作机制,并为性能优化和插件开发提供指导。

Compiler 与 Compilation 的关系

核心关系分析

Compiler 和 Compilation 之间存在明确的层次关系和职责划分:

// Compiler 和 Compilation 关系示例
class CompilerCompilationRelationship {
  constructor() {
    this.compiler = null;
    this.currentCompilation = null;
  }
  
  // 演示 Compiler 如何管理 Compilation
  demonstrateRelationship() {
    // 1. Compiler 是单例,整个构建过程只有一个
    this.compiler = new Compiler('/project/root');
    
    // 2. Compiler 负责创建 Compilation 实例
    const compilation1 = this.compiler.newCompilation();
    console.log('第一次编译:', compilation1.name);
    
    // 3. 在监听模式下,文件变化会创建新的 Compilation
    const compilation2 = this.compiler.newCompilation();
    console.log('第二次编译:', compilation2.name);
    
    // 4. 每个 Compilation 都有对 Compiler 的引用
    console.log('Compilation 的 Compiler 引用:', compilation1.compiler === this.compiler);
  }
  
  // Compiler 和 Compilation 的数据流
  dataFlowExample() {
    const compiler = new Compiler('/project');
    
    // Compiler 的配置传递给 Compilation
    compiler.options = {
      entry: './src/index.js',
      output: {
        path: '/dist',
        filename: 'bundle.js'
      },
      module: {
        rules: [
          {
            test: /\.js$/,
            use: 'babel-loader'
          }
        ]
      }
    };
    
    // 创建 Compilation 时传递必要信息
    const compilation = compiler.newCompilation({
      normalModuleFactory: new NormalModuleFactory(compiler.options),
      contextModuleFactory: new ContextModuleFactory()
    });
    
    // Compilation 使用 Compiler 的配置和资源
    compilation.outputOptions = compiler.options.output;
    compilation.resolverFactory = compiler.resolverFactory;
    compilation.inputFileSystem = compiler.inputFileSystem;
    compilation.outputFileSystem = compiler.outputFileSystem;
    
    return { compiler, compilation };
  }
}

生命周期同步机制

// Compiler 和 Compilation 生命周期同步
class LifecycleSynchronization {
  constructor() {
    this.compiler = new Compiler();
    this.setupLifecycleSync();
  }
  
  setupLifecycleSync() {
    // Compiler 创建 Compilation 时的钩子同步
    this.compiler.hooks.thisCompilation.tap('LifecycleSync', (compilation, params) => {
      console.log('Compiler 创建了新的 Compilation');
      
      // 在 Compilation 的各个阶段添加监听
      compilation.hooks.buildModule.tap('CompilerSync', (module) => {
        console.log(`Compiler 监听到模块构建: ${module.resource}`);
      });
      
      compilation.hooks.seal.tap('CompilerSync', () => {
        console.log('Compiler 监听到 Compilation 开始封装');
      });
      
      compilation.hooks.optimizeChunks.tap('CompilerSync', (chunks) => {
        console.log(`Compiler 监听到代码块优化: ${chunks.size} 个代码块`);
      });
    });
    
    // Compilation 完成后的处理
    this.compiler.hooks.afterCompile.tapAsync('LifecycleSync', (compilation, callback) => {
      console.log('Compilation 完成,Compiler 开始后续处理');
      
      // Compiler 可以访问 Compilation 的结果
      const stats = {
        modules: compilation.modules.size,
        chunks: compilation.chunks.size,
        assets: Object.keys(compilation.assets).length,
        errors: compilation.errors.length,
        warnings: compilation.warnings.length
      };
      
      console.log('编译统计:', stats);
      callback();
    });
  }
  
  // 演示数据传递
  demonstrateDataPassing() {
    const compiler = this.compiler;
    
    compiler.run((err, stats) => {
      if (err) {
        console.error('编译失败:', err);
        return;
      }
      
      // stats 包含了 Compilation 的所有信息
      console.log('编译成功:', {
        hash: stats.hash,
        version: stats.version,
        time: stats.endTime - stats.startTime,
        assets: stats.assets.length,
        chunks: stats.chunks.length,
        modules: stats.modules.length,
        errors: stats.errors.length,
        warnings: stats.warnings.length
      });
    });
  }
}

实践应用与优化

性能监控插件

// 基于 Compiler 和 Compilation 的性能监控插件
class PerformanceMonitorPlugin {
  constructor(options = {}) {
    this.options = {
      outputFile: 'build-performance.json',
      threshold: {
        buildTime: 5000,      // 构建时间阈值(ms)
        moduleCount: 1000,    // 模块数量阈值
        chunkCount: 50,       // chunk数量阈值
        assetSize: 1024 * 1024 // 资源大小阈值(bytes)
      },
      ...options
    };
    
    this.metrics = {
      compiler: {},
      compilations: []
    };
  }
  
  apply(compiler) {
    const pluginName = PerformanceMonitorPlugin.name;
    
    // 监控 Compiler 生命周期
    this.setupCompilerMonitoring(compiler, pluginName);
    
    // 监控 Compilation 生命周期
    compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
      this.setupCompilationMonitoring(compilation, pluginName);
    });
    
    // 输出性能报告
    compiler.hooks.done.tap(pluginName, (stats) => {
      this.generatePerformanceReport(stats);
    });
  }
  
  setupCompilerMonitoring(compiler, pluginName) {
    let compilerStartTime;
    
    // Compiler 启动
    compiler.hooks.beforeRun.tap(pluginName, () => {
      compilerStartTime = Date.now();
      this.metrics.compiler.startTime = compilerStartTime;
      console.log('🚀 编译器启动');
    });
    
    // 编译完成
    compiler.hooks.done.tap(pluginName, (stats) => {
      const endTime = Date.now();
      const duration = endTime - compilerStartTime;
      
      this.metrics.compiler.endTime = endTime;
      this.metrics.compiler.duration = duration;
      this.metrics.compiler.totalCompilations = this.metrics.compilations.length;
      
      console.log(`✅ 编译完成,总耗时: ${duration}ms`);
      
      // 检查性能阈值
      this.checkPerformanceThresholds(stats, duration);
    });
  }
  
  setupCompilationMonitoring(compilation, pluginName) {
    const compilationMetrics = {
      startTime: Date.now(),
      modules: { total: 0, built: 0, cached: 0, failed: 0 },
      chunks: { total: 0, initial: 0, async: 0 },
      assets: { total: 0, size: 0 },
      phases: {}
    };
    
    // 模块构建监控
    compilation.hooks.buildModule.tap(pluginName, (module) => {
      compilationMetrics.modules.total++;
      if (!compilationMetrics.phases.moduleBuilding) {
        compilationMetrics.phases.moduleBuilding = Date.now();
      }
    });
    
    compilation.hooks.succeedModule.tap(pluginName, (module) => {
      if (module.built) {
        compilationMetrics.modules.built++;
      } else {
        compilationMetrics.modules.cached++;
      }
    });
    
    // 封装阶段监控
    compilation.hooks.seal.tap(pluginName, () => {
      compilationMetrics.phases.seal = Date.now();
      console.log('🔒 开始封装阶段');
    });
    
    // Chunk 创建监控
    compilation.hooks.afterChunks.tap(pluginName, (chunks) => {
      compilationMetrics.phases.chunksCreated = Date.now();
      compilationMetrics.chunks.total = chunks.size;
      
      let initialChunks = 0;
      let asyncChunks = 0;
      
      for (const chunk of chunks) {
        if (chunk.isOnlyInitial()) {
          initialChunks++;
        } else {
          asyncChunks++;
        }
      }
      
      compilationMetrics.chunks.initial = initialChunks;
      compilationMetrics.chunks.async = asyncChunks;
      
      console.log(`📋 Chunk 创建完成: ${chunks.size}`);
    });
    
    // 记录完成时间
    compilation.hooks.statsFactory.tap(pluginName, () => {
      compilationMetrics.endTime = Date.now();
      compilationMetrics.duration = compilationMetrics.endTime - compilationMetrics.startTime;
      this.metrics.compilations.push(compilationMetrics);
      
      console.log(`🏁 Compilation 完成,耗时: ${compilationMetrics.duration}ms`);
    });
  }
  
  checkPerformanceThresholds(stats, duration) {
    const warnings = [];
    const { threshold } = this.options;
    
    // 检查构建时间
    if (duration > threshold.buildTime) {
      warnings.push(`构建时间过长: ${duration}ms (阈值: ${threshold.buildTime}ms)`);
    }
    
    // 检查模块数量
    if (stats.compilation.modules.size > threshold.moduleCount) {
      warnings.push(`模块数量过多: ${stats.compilation.modules.size} (阈值: ${threshold.moduleCount})`);
    }
    
    // 输出警告
    if (warnings.length > 0) {
      console.log('\n⚠️  性能警告:');
      warnings.forEach(warning => console.log(`   ${warning}`));
    }
    
    return warnings;
  }
  
  generatePerformanceReport(stats) {
    const report = {
      timestamp: new Date().toISOString(),
      compiler: this.metrics.compiler,
      compilations: this.metrics.compilations,
      summary: this.generateSummary(stats)
    };
    
    // 输出到文件
    const reportJson = JSON.stringify(report, null, 2);
    stats.compilation.assets[this.options.outputFile] = {
      source: () => reportJson,
      size: () => reportJson.length
    };
    
    console.log(`\n📊 性能报告已生成: ${this.options.outputFile}`);
  }
  
  generateSummary(stats) {
    const lastCompilation = this.metrics.compilations[this.metrics.compilations.length - 1];
    
    return {
      totalDuration: this.metrics.compiler.duration,
      compilationCount: this.metrics.compilations.length,
      modules: {
        total: stats.compilation.modules.size,
        built: lastCompilation?.modules.built || 0,
        cached: lastCompilation?.modules.cached || 0
      },
      chunks: {
        total: stats.compilation.chunks.size,
        initial: lastCompilation?.chunks.initial || 0,
        async: lastCompilation?.chunks.async || 0
      },
      assets: {
        count: Object.keys(stats.compilation.assets).length,
        size: lastCompilation?.assets.size || 0
      }
    };
  }
}

总结

通过深入分析 Webpack 的 Compiler 和 Compilation,我们可以得出以下关键认识:

核心差异

  1. 生命周期:Compiler 贯穿整个 Webpack 运行周期,而 Compilation 代表单次编译过程
  2. 职责分工:Compiler 负责环境搭建和流程控制,Compilation 负责具体的编译工作
  3. 数据管理:Compiler 管理全局配置和状态,Compilation 管理编译过程中的模块和资源

架构优势

  1. 职责分离:清晰的职责划分使得代码更容易维护和扩展
  2. 插件友好:丰富的钩子系统为插件开发提供了强大的扩展能力
  3. 性能优化:通过缓存和增量编译机制提升构建性能

实践建议

  1. 插件开发:根据需求选择合适的钩子,理解 Compiler 和 Compilation 的不同阶段
  2. 性能优化:利用缓存机制和并行处理提升构建效率
  3. 监控分析:通过性能监控插件了解构建瓶颈,持续优化构建流程

理解 Compiler 和 Compilation 的工作原理,对于前端架构师来说是必备技能,它不仅有助于我们更好地使用 Webpack,还能让我们在遇到构建问题时快速定位和解决问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值