Webpack 构建速度优化

Webpack 构建速度优化是一个系统工程,需要从多个维度进行优化。以下是全面的优化方案:

1. 分析工具先行

1.1 构建速度分析

# 安装速度分析插件
npm install --save-dev speed-measure-webpack-plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

module.exports = smp.wrap({
  // webpack 配置
  plugins: [
    // ... 其他插件
  ]
})

1.2 打包体积分析

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'server',
      openAnalyzer: true
    })
  ]
}

1.3 构建过程分析

const { ProgressPlugin } = require('webpack')

module.exports = {
  plugins: [
    new ProgressPlugin({
      activeModules: true, // 显示活跃模块
      entries: true, // 显示入口
      modules: true, // 显示模块
      dependencies: true, // 显示依赖
    })
  ]
}

2. 优化解析和编译

2.1 缩小文件搜索范围

module.exports = {
  resolve: {
    // 明确第三方模块路径
    modules: [path.resolve(__dirname, 'node_modules')],
    // 减少后缀尝试
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    // 使用别名避免层层查找
    alias: {
      '@': path.resolve(__dirname, 'src'),
      'react': path.resolve(__dirname, './node_modules/react'),
    }
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        // 只处理 src 目录下的文件
        include: path.resolve(__dirname, 'src'),
        use: ['babel-loader']
      }
    ]
  }
}

2.2 避免不必要的解析

module.exports = {
  module: {
    // 不解析已知的预编译库
    noParse: /jquery|lodash|moment/,
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true, // 开启 babel 缓存
            cacheCompression: false // 关闭缓存压缩
          }
        }
      }
    ]
  }
}

3. 利用缓存机制

3.1 Webpack 5 持久化缓存

module.exports = {
  cache: {
    type: 'filesystem', // 使用文件系统缓存
    buildDependencies: {
      config: [__filename] // 构建依赖配置
    },
    cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack')
  }
}

3.2 Webpack 4 缓存方案

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

module.exports = {
  plugins: [
    new HardSourceWebpackPlugin(),
    // 或者使用 cache-loader
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'cache-loader',
            options: {
              cacheDirectory: path.resolve('.cache')
            }
          },
          'babel-loader'
        ]
      }
    ]
  }
}

4. 多进程/多实例构建

4.1 Thread Loader (推荐)

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader',
            options: {
              workers: require('os').cpus().length - 1, // CPU 核心数 - 1
              poolTimeout: Infinity // 开发模式保持进程运行
            }
          },
          'babel-loader'
        ]
      }
    ]
  }
}

4.2 Terser 多进程压缩

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

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: true, // 开启多进程
        terserOptions: {
          compress: {
            drop_console: true // 生产环境移除 console
          }
        }
      })
    ]
  }
}

5. 代码分割与按需加载

5.1 动态导入

// 路由懒加载
const Home = () => import(/* webpackChunkName: "home" */ './views/Home.vue')
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue')

// 组件懒加载
const HeavyComponent = () => import('./components/HeavyComponent.vue')

5.2 SplitChunks 优化

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
          chunks: 'all'
        },
        common: {
          name: 'common',
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true
        }
      }
    }
  }
}

6. DLL 预编译(适用于大型项目)

6.1 创建 DLL 配置

// webpack.dll.config.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
  mode: 'production',
  entry: {
    vendor: ['react', 'react-dom', 'lodash', 'moment']
  },
  output: {
    path: path.resolve(__dirname, 'dll'),
    filename: '[name].dll.js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]_library',
      path: path.join(__dirname, 'dll', '[name]-manifest.json')
    })
  ]
}

6.2 使用 DLL

// webpack.config.js
const webpack = require('webpack')

module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: require('./dll/vendor-manifest.json')
    })
  ]
}

7. 开发环境优化

7.1 增量编译

module.exports = {
  watch: true,
  watchOptions: {
    ignored: /node_modules/, // 忽略 node_modules
    aggregateTimeout: 300, // 防抖延迟
    poll: 1000 // 轮询间隔
  }
}

7.2 热更新优化

const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')

module.exports = {
  devServer: {
    hot: true,
    port: 8080,
    compress: true, // 开启 gzip
    overlay: true, // 显示错误覆盖层
    stats: 'minimal' // 减少控制台输出
  },
  plugins: [
    new ReactRefreshWebpackPlugin() // React 热更新
  ]
}

8. 生产环境优化

8.1 资源压缩

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin(), // JS 压缩
      new CssMinimizerPlugin() // CSS 压缩
    ]
  },
  plugins: [
    new CompressionPlugin({ // Gzip 压缩
      test: /\.(js|css|html|svg)$/,
      threshold: 8192
    })
  ]
}

8.2 预编译资源

const PrepackWebpackPlugin = require('prepack-webpack-plugin').default

module.exports = {
  plugins: [
    // 预编译常量
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    }),
    // 预编译资源(可选)
    new PrepackWebpackPlugin()
  ]
}

9. 高级优化技巧

9.1 模块联邦 (Webpack 5)

// app1 webpack.config.js
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button'
      },
      shared: ['react', 'react-dom']
    })
  ]
}

9.2 资源内联

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192, // 8KB 以下转 base64
              fallback: 'file-loader'
            }
          }
        ]
      }
    ]
  }
}

10. 监控与持续优化

10.1 构建监控

// 构建耗时监控
class BuildTimePlugin {
  apply(compiler) {
    let startTime
    compiler.hooks.beforeRun.tap('BuildTimePlugin', () => {
      startTime = Date.now()
    })
    
    compiler.hooks.done.tap('BuildTimePlugin', () => {
      const endTime = Date.now()
      console.log(`构建耗时: ${(endTime - startTime) / 1000}秒`)
    })
  }
}

10.2 配置文件优化建议

// 根据环境使用不同配置
module.exports = (env, argv) => {
  const isProduction = argv.mode === 'production'
  
  return {
    mode: argv.mode || 'development',
    devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map',
    // 其他配置...
  }
}

优化效果评估

实施这些优化后,通常可以看到:

  • 开发环境:冷启动时间减少 50-70%,热更新时间减少 60-80%
  • 生产环境:构建时间减少 30-50%,打包体积减少 20-40%

建议根据项目实际情况选择适合的优化方案,优先解决性能瓶颈最严重的环节。

posted @ 2025-10-13 19:48  阿木隆1237  阅读(22)  评论(0)    收藏  举报