【webpack】webpack优化

2023/01/06 17:34:43

量化分析

speed-measure-webpack-plugin

speed-measure-webpack-plugin 插件可以测量各个插件和 loader 所花费的时间,用这个插件可以很好的判断优化效果。

webpack 5 中使用会打印大量 error 信息,并且会导致 cache 不生效。

无法与 HardSourceWebpackPlugin 插件一起使用。

  • 安装
npm i speed-measure-webpack-plugin -D
  • 使用

直接用 SpeedMeasurePlugin 的实例包裹 webpack 配置即可。

//webpack.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();

const config = {
  //...webpack配置
};

module.exports = smp.wrap(config);

使用之后,构建时会多打印一些信息,示例如下:

SMP  ⏱
General output time took 27.43 secs

 SMP  ⏱  Plugins
TerserPlugin took 11.92 secs
HtmlWebpackPlugin took 0.702 secs
CopyPlugin took 0.097 secs

 SMP  ⏱  Loaders
thread-loader, and
babel-loader took 12.78 secs
  module count = 119
mini-css-extract-plugin, and
css-loader, and
postcss-loader, and
less-loader took 0.059 secs
  module count = 32

webpack-bundle-analyzer

webpack-bundle-analyzer 插件可以生成依赖分析报告,可以直观地分析打包出来的文件有那些及它们的大小。

  • 安装
npm install webpack-bundle-analyzer -D
  • 配置
//webpack.config.prod.js
const BundleAnalyzerPlugin =
  require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const merge = require("webpack-merge");
const baseWebpackConfig = require("./webpack.config.base");
module.exports = merge(baseWebpackConfig, {
  //....
  plugins: [
    //...
    new BundleAnalyzerPlugin(),
  ],
});

缩减 loader 的校验范围

我们可以通过 exclude、include 配置来确保转译尽可能少的文件。顾名思义,exclude 指定要排除的文件,include 指定要包含的文件。

exclude 的优先级高于 include,在 include 和 exclude 中使用绝对路径数组,尽量避免 exclude,更倾向于使用 include。

//webpack.config.js
const path = require("path");
module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js[x]?$/,
        use: ["babel-loader"],
        include: [path.resolve(__dirname, "src")],
      },
    ],
  },
};

缓存 loader

在一些性能开销较大的 loader 之前添加 cache-loader,将结果缓存中磁盘中。默认保存在 node_modueles/.cache/cache-loader 目录下。

保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用 cache-loader

  • 安装
npm install cache-loader -D
  • 配置

在需要缓存的 loader 之前添加 cache-loader

module.exports = {
  //...
  module: {
    //我的项目中,babel-loader耗时比较长,所以我给它配置了`cache-loader`
    rules: [
      {
        test: /\.jsx?$/,
        use: ["cache-loader", "babel-loader"],
      },
    ],
  },
};

缓存模块和 chunk

webpack 5 提供了 cache 选项以开启缓存,默认开发环境时被设置为 type:'memory',生产环境中被禁用。

在生产环境下配置 type 为 filesystem 可以实现持久化缓存,webpack 会在第一次构建时生成缓存文件,在后续构建时用以改善构建速度。

默认缓存位置为:node_modules/.cache/webpack

其他配置见webpack cache 官方文档open in new window

exports.module = {
  cache: {
    type: "filesystem",
  },
};

在更低版本中 HardSourceWebpackPlugin 插件也可达到同样的效果,见带你深度解锁 Webpack 系列(优化篇)open in new window关于 HardSourceWebpackPlugin 的内容。

多进程打包

把 thread-loader 放置在其它 loader 之前,那么放置在这个 loader 之后的 loader 就会在一个单独的 worker 池中运行。

在 worker 池(worker pool)中运行的 loader 是受到限制的。例如:

  1. 这些 loader 不能产生新的文件。
  2. 这些 loader 不能使用定制的 loader API(也就是说,通过插件)。
  3. 这些 loader 无法获取 webpack 的选项设置。
  • 安装
npm install thread-loader -D
  • 配置
module.exports = {
  module: {
    //我的项目中,babel-loader耗时比较长,所以我给它配置 thread-loader
    rules: [
      {
        test: /\.jsx?$/,
        use: ["thread-loader", "cache-loader", "babel-loader"],
      },
    ],
  },
};

标志无需转化和解析的模块

如果一些第三方模块没有 AMD/CommonJS 规范版本,可以使用 noParse 来标识这个模块,这样 Webpack 会引入这些模块,但是不进行转化和解析,从而提升 Webpack 的构建性能 ,例如:jquery 、lodash。

  • 配置
//webpack.config.js
module.exports = {
  //...
  module: {
    noParse: /jquery|lodash/,
  },
};

引入线上资源而不是打入包中

对于一些第三方库(jquery),可以通过加载 jquery 的 cdn 链接来引入而不是全部下载到本地,这样就可以减少打包出来的 js 体积。

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>

我们希望在使用时,仍然可以通过 import 的方式去引用(如 import $ from 'jquery'),并且希望 webpack 不会对其进行打包,此时就可以配置 externals。

//webpack.config.js
module.exports = {
  //...
  externals: {
    //jquery通过script引入之后,全局中即有了 jQuery 变量
    jquery: "jQuery",
  },
};

单独打包不常变化的依赖

如果将所有 js 文件都打包,会导致最终生产的 js 文件很大。

对于不常变化的依赖,可以将其单独编译打包,当这些依赖没有变化时,就不需要重新编译。

感觉这种方法不如直接用上一个方法将依赖用 CDN 加载。

带你深度解锁 Webpack 系列(优化篇)open in new window 中的 DllPlugin 部分。

抽离公共代码

抽离公共代码是对于多入口应用来说的,如果多个页面引入了一些公共模块,那么可以把这些公共的模块抽离出来,单独打包。

公共代码只需要下载一次就缓存起来了,避免了重复下载。

//webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      //分割代码块
      cacheGroups: {
        vendor: {
          //第三方依赖
          priority: 1, //设置优先级,首先抽离第三方模块
          name: "vendor",
          test: /node_modules/,
          chunks: "initial",
          minSize: 0,
          minChunks: 1, //最少引入了1次
        },
        //缓存组
        common: {
          //公共模块
          chunks: "initial",
          name: "common",
          minSize: 100, //大小超过100个字节
          minChunks: 3, //最少引入了3次
        },
      },
    },
  },
};

参考

带你深度解锁 Webpack 系列(优化篇)open in new window

webpack 官网open in new window