CodeWalk

Webpack 自定义 Plugin 实现(构建时输出文件列表)

作者:小字辈 · 2026-05-30 12:55

请手写一个自定义 Webpack Plugin,在构建完成后输出所有生成的文件名和大小到一个 summary.json 文件中。

回答

小字辈

// FileListPlugin.js
const fs = require('fs');
const path = require('path');

class FileListPlugin {
  constructor(options = {}) {
    this.filename = options.filename || 'filelist.json';
  }

  apply(compiler) {
    // emit 钩子在生成资源到 output 目录之前触发
    compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, callback) => {
      // compilation.assets 包含所有即将输出的文件
      const fileList = [];
      
      for (const [filename, asset] of Object.entries(compilation.assets)) {
        fileList.push({
          name: filename,
          size: asset.size(),
          sizeFormatted: `${(asset.size() / 1024).toFixed(2)} KB`,
        });
      }
      
      // 按大小降序排列
      fileList.sort((a, b) => b.size - a.size);
      
      // 生成 summary 资源
      const content = JSON.stringify({
        total: fileList.length,
        totalSize: fileList.reduce((sum, f) => sum + f.size, 0),
        files: fileList,
        timestamp: new Date().toISOString(),
      }, null, 2);
      
      // 添加到 compilation.assets 输出
      compilation.assets[this.filename] = {
        source: () => content,
        size: () => content.length,
      };
      
      callback();
    });
    
    // done 钩子在构建完全结束后触发
    compiler.hooks.done.tap('FileListPlugin', (stats) => {
      console.log(`📦 文件列表已生成: ${this.filename}`);
    });
  }
}

module.exports = FileListPlugin;

使用

const FileListPlugin = require('./plugins/FileListPlugin');

module.exports = {
  plugins: [
    new FileListPlugin({ filename: 'summary.json' }),
  ],
};

生命周期钩子

  • compile/thisCompilation/compilation — 编译阶段
  • make — 开始模块构建
  • emit — 输出资源前(可修改 assets)
  • afterEmit — 输出完成后
  • done — 构建完成