Skip to content

webpack 5 优化

编译速度分析

通过 speed-measure-webpack-plugin 插件进行构建速度分析,可以看到各个 loader、plugin 的构建时长,后续可针对耗时 loader、plugin 进行优化。

安装:

bash
npm i -D speed-measure-webpack-plugin

webpack.dev.js 配置方式如下:

javascript
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
  // ...webpack config...
})

打包体积分析

使用 webpack-bundle-analyzer 查看打包后生成的 bundle 体积分析,将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。帮助我们分析输出结果来检查 模块在何处结束。

安装:

bash
npm i -D webpack-bundle-analyzer

webpack.prod.js 配置方式如下:

javascript
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
  plugins: [
    // 打包体积分析
    new BundleAnalyzerPlugin(),
  ],
}

加快构建速度

  • 通过配置 webpack 持久化缓存cache: filesystem,来缓存生成的 webpack 模块和 chunk,改善构建速度。

简单来说,通过 cache: filesystem 可以将构建过程的 webpack 模板进行缓存,大幅提升二次构建速度、打包速度,当构建突然中断 ,二次进行构建时,可以直接从缓存中拉取,可提速 90% 左右。

webpack.common.js 配置方式如下:

javascript
module.exports = {
  cache: {
    type: 'filesystem', // 使用文件缓存
  },
}
  • 减少 loader、plugins

每个的 loader、plugin 都有其启动时间。尽量少地使用工具,将非必须的 loader、plugins 删除。

  • 指定 include

为 loader 指定 include,减少 loader 应用范围,仅应用于最少数量的必要模块。

webpack.common.js 配置方式如下:

javascript
module.exports = {
  rules: [
    {
      test: /\.(js|ts|jsx|tsx)$/,
      include: paths.appSrc,
      use: [
        {
          loader: 'esbuild-loader',
          options: {
            loader: 'tsx',
            target: 'es2015',
          },
        },
      ],
    },
  ],
}
  • 使用 webpack 资源模块 (asset module) 代替旧的 assets loader(如 file-loader/url-loader/raw-loader 等),减少 loader 配置数量。

配置方式如下:

javascript
module.exports = {
  rules: [
    {
      test: /\.(png|svg|jpg|jpeg|gif)$/i,
      include: [paths.appSrc],
      type: 'asset/resource',
    },
  ],
}
  • alias

alias 可以创建 importrequire 的别名,用来简化模块引入。

webpack.common.js 配置方式如下:

javascript
module.exports = {
  resolve: {
    alias: {
      '@': paths.appSrc, // @ 代表 src 路径
    },
  },
}
  • extensions

extensions 表示需要解析的文件类型列表。

根据项目中的文件类型,定义 extensions,以覆盖 webpack 默认的 extensions,加快解析速度。

由于 webpack 的解析顺序是从左到右,因此要将使用频率高的文件类型放在左侧,如下我将 tsx 放在最左侧。

webpack.common.js 配置方式如下:

javascript
module.exports = {
  resolve: {
    extensions: ['.tsx', '.js'], // 因为我的项目只有这两种类型的文件,如果有其他类型,需要添加进去。
  },
}
  • modules

modules 表示 webpack 解析模块时需要解析的目录。

指定目录可缩小 webpack 解析范围,加快构建速度。

webpack.common.js 配置方式如下:

javascript
module.exports = {
  modules: ['node_modules', paths.appSrc],
}
  • thread-loader

通过 thread-loader 将耗时的 loader 放在一个独立的 worker 池中运行,加快 loader 构建速度。

安装:

bash
npm i -D thread-loader

webpack.common.js 配置方式如下:

javascript
module.exports = {
  rules: [
    {
      test: /\.module\.(scss|sass)$/,
      include: paths.appSrc,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            modules: true,
            importLoaders: 2,
          },
        },
        {
          loader: 'postcss-loader',
          options: {
            postcssOptions: {
              plugins: [['postcss-preset-env']],
            },
          },
        },
        {
          loader: 'thread-loader',
          options: {
            workerParallelJobs: 2,
          },
        },
        'sass-loader',
      ].filter(Boolean),
    },
  ],
}

减小打包体积

1. 代码压缩

体积优化第一步是压缩代码,通过 webpack 插件,将 JS、CSS 等文件进行压缩。

1.1 JS 压缩

使用 TerserWebpackPlugin 来压缩 JavaScript。

webpack5 自带最新的 terser-webpack-plugin,无需手动安装。

terser-webpack-plugin 默认开启了 parallel: true 配置,并发运行的默认数量: os.cpus().length - 1 ,本文配置的 parallel 数量为 4,使用多进程并发运行压缩以提高构建速度。

webpack.prod.js 配置方式如下:

javascript
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        parallel: 4,
        terserOptions: {
          parse: {
            ecma: 8,
          },
          compress: {
            ecma: 5,
            warnings: false,
            comparisons: false,
            inline: 2,
          },
          mangle: {
            safari10: true,
          },
          output: {
            ecma: 5,
            comments: false,
            ascii_only: true,
          },
        },
      }),
    ],
  },
}

1.2 CSS 压缩

使用 CssMinimizerWebpackPlugin 压缩 CSS 文件。

optimize-css-assets-webpack-plugin 相比 ,css-minimizer-webpack-plugin 在 source maps 和 assets 中使用查询字符串会更加准确,而且支持缓存和并发模式下运行。

CssMinimizerWebpackPlugin 将在 Webpack 构建期间搜索 CSS 文件,优化、压缩 CSS。

安装:

npm install -D css-minimizer-webpack-plugin

webpack.prod.js 配置方式如下:

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

module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin({
        parallel: 4,
      }),
    ],
  },
}

由于 CSS 默认是放在 JS 文件中,因此本示例是基于下章节将 CSS 代码分离后的效果。

2. 代码分离

代码分离能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控 制资源加载优先级,可以缩短页面加载时间。

2.1 抽离重复代码

SplitChunksPlugin 插件开箱即用,可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

webpack 将根据以下条件自动拆分 chunks:

  • 新的 chunk 可以被共享,或者模块来自于 node_modules 文件夹;
  • 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积);
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 30;
  • 当加载初始化页面时,并发请求的最大数量小于或等于 30; 通过 splitChunks 把 react 等公共库抽离出来,不重复引入占用体积。

注意:切记不要为 cacheGroups 定义固定的 name,因为 cacheGroups.name 指定字符串或始终返回相同字符串的函数时,会将所有常 见模块和 vendor 合并为一个 chunk。这会导致更大的初始下载量并减慢页面加载速度。

webpack.prod.js 配置方式如下:

javascript
module.exports = {
  splitChunks: {
    // include all types of chunks
    chunks: 'all',
    // 重复打包问题
    cacheGroups: {
      vendors: {
        // node_modules里的代码
        test: /[\\/]node_modules[\\/]/,
        chunks: 'all',
        // name: 'vendors', 一定不要定义固定的name
        priority: 10, // 优先级
        enforce: true,
      },
    },
  },
}