2025年11月20日6 分钟

Webpack 5 构建提速方案:从分钟级到秒级

系统性梳理 Webpack 5 构建速度优化的 10+ 种方案,涵盖缓存、并行、缩小范围、DLL 等核心策略

为什么 Webpack 构建慢

Webpack 构建的本质是:遍历所有模块 → 对每个模块执行 Loader 转换 → 生成依赖图 → 打包输出。慢的原因主要有三个:

1. 文件太多:几千个模块需要逐一处理
2. 转换太慢:Babel/TypeScript 编译是 CPU 密集型操作
3. 重复工作:每次构建都从头开始,不复用上次结果

一、持久化缓存(最大提速)

Webpack 5 最重要的新特性——把构建结果缓存到磁盘,第二次构建直接复用。

// webpack.config.js
module.exports = {
  cache: {
    type: 'filesystem',  // 关键:开启磁盘缓存
    buildDependencies: {
      config: [__filename],  // 配置文件变化时使缓存失效
    },
    cacheDirectory: path.resolve(__dirname, '.webpack_cache'),
    version: '1.0',  // 手动版本号,改变时清空缓存
  },
}
效果对比:
  首次构建: 45s
  第二次(缓存命中): 3s  ← 提速 15 倍 🚀
  修改一个文件后: 5s     ← 只重新编译变化的模块

缓存失效策略

cache: {
  type: 'filesystem',

  // 当这些文件变化时,缓存完全失效
  buildDependencies: {
    config: [__filename],       // webpack 配置文件
    tsconfig: [path.resolve(__dirname, 'tsconfig.json')],
  },

  // 当这些值变化时,缓存失效
  version: createHash('md4')
    .update(process.env.NODE_ENV)
    .update(JSON.stringify(process.env))
    .digest('hex'),
}

二、缩小构建范围

1. 精确 include/exclude

module: {
  rules: [
    {
      test: /\.(ts|tsx)$/,
      // ✅ 只处理 src 目录
      include: path.resolve(__dirname, 'src'),
      // ✅ 排除 node_modules(它们已经编译过了)
      exclude: /node_modules/,
      use: 'swc-loader',
    },
  ],
},

2. resolve 优化

resolve: {
  // ✅ 减少后缀尝试次数(按频率排序)
  extensions: ['.tsx', '.ts', '.js'],  // 不要写 .json .css 等

  // ✅ 指定第三方包入口
  mainFields: ['browser', 'module', 'main'],

  // ✅ 路径别名避免深层遍历
  alias: {
    '@': path.resolve(__dirname, 'src'),
  },

  // ✅ 只在 src 和 node_modules 中查找模块
  modules: [path.resolve(__dirname, 'src'), 'node_modules'],
},

3. 跳过不需要解析的模块

module: {
  // 这些库没有依赖其他模块,不需要解析
  noParse: /jquery|lodash|moment/,
},

三、并行处理

1. thread-loader(多线程 Loader)

module: {
  rules: [
    {
      test: /\.(ts|tsx)$/,
      use: [
        {
          loader: 'thread-loader',
          options: {
            workers: require('os').cpus().length - 1,  // CPU 核心数 - 1
            workerParallelJobs: 50,
            poolTimeout: 2000,
          },
        },
        'babel-loader',
      ],
    },
  ],
},

2. TerserPlugin 并行压缩

const TerserPlugin = require('terser-webpack-plugin')

optimization: {
  minimizer: [
    new TerserPlugin({
      parallel: true,  // 多进程并行压缩
      terserOptions: {
        compress: {
          drop_console: true,  // 移除 console.log
        },
      },
    }),
  ],
},

四、使用更快的工具替代

1. SWC 替代 Babel(快 20-70 倍)

module: {
  rules: [
    {
      test: /\.(ts|tsx)$/,
      // ❌ babel-loader: 单文件编译 50ms
      // ✅ swc-loader: 单文件编译 2ms
      use: {
        loader: 'swc-loader',
        options: {
          jsc: {
            parser: { syntax: 'typescript', tsx: true },
            transform: { react: { runtime: 'automatic' } },
          },
        },
      },
    },
  ],
},

2. esbuild-loader 替代 Babel + Terser

const { EsbuildPlugin } = require('esbuild-loader')

module: {
  rules: [{
    test: /\.(ts|tsx)$/,
    loader: 'esbuild-loader',
    options: { target: 'es2020' },
  }],
},
optimization: {
  minimizer: [
    new EsbuildPlugin({ target: 'es2020' }),  // esbuild 压缩比 Terser 快 100 倍
  ],
},

五、DLL 预编译(大型项目)

把不常变动的第三方库预先编译,避免每次都重新打包:

// webpack.dll.config.js — 只运行一次
module.exports = {
  entry: {
    vendor: ['react', 'react-dom', 'lodash'],
  },
  output: {
    path: path.resolve(__dirname, 'dll'),
    filename: '[name].dll.js',
    library: '[name]_dll',
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]_dll',
      path: path.resolve(__dirname, 'dll/[name].manifest.json'),
    }),
  ],
}
// webpack.config.js — 正常构建引用 DLL
plugins: [
  new webpack.DllReferencePlugin({
    manifest: require('./dll/vendor.manifest.json'),
  }),
],

六、代码分割优化

optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      // 第三方库单独打包(变动少,缓存命中率高)
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        priority: 10,
        chunks: 'all',
      },
      // 被 2 个以上页面共享的模块
      commons: {
        minChunks: 2,
        name: 'commons',
        priority: 5,
        reuseExistingChunk: true,
      },
    },
  },
},

七、开发模式专项优化

// 开发模式配置
module.exports = {
  mode: 'development',

  // ✅ eval 速度最快(不需要 Source Map 精确到列)
  devtool: 'eval-cheap-module-source-map',

  // ✅ 开发时不压缩
  optimization: {
    minimize: false,
    removeAvailableModules: false,
    removeEmptyChunks: false,
    splitChunks: false,
  },

  // ✅ 增量编译
  watchOptions: {
    ignored: /node_modules/,  // 不监听 node_modules
    aggregateTimeout: 300,    // 300ms 内的变更合并处理
  },

  // ✅ 只输出变化的模块
  output: {
    pathinfo: false,  // 减少生成代码的信息注释
  },
}

八、构建分析

# 速度分析
npm install speed-measure-webpack-plugin -D

# 体积分析
npm install webpack-bundle-analyzer -D
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

const smp = new SpeedMeasurePlugin()

module.exports = smp.wrap({
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
    }),
  ],
})
Speed Measure Plugin 输出:
  SWC Loader: 2.3s
  CSS Loader: 1.1s
  TerserPlugin: 5.2s
  Total: 12.8s

→ TerserPlugin 最慢 → 替换为 esbuild-loader → 降到 0.5s

优化清单总结

方案提速效果适用阶段复杂度
filesystem cache⭐⭐⭐⭐⭐开发+生产
SWC/esbuild 替代 Babel⭐⭐⭐⭐开发+生产
缩小 include/exclude⭐⭐⭐开发+生产
thread-loader 并行⭐⭐⭐生产
resolve 优化⭐⭐开发+生产
DLL 预编译⭐⭐⭐开发
devtool 选择⭐⭐开发
esbuild 压缩⭐⭐⭐⭐生产

优先做前三项,就能覆盖 80% 的提速效果。