Skip to content

优化 loader 性能

进一步限制 loader 的应用范围

思路是:对于某些库,不使用 loader

例如:babel-loader 可以转换 ES6 或更高版本的语法,可是有些库本身就是用 ES5 语法书写的,不需要转换,使用 babel-loader 反而会浪费构建时间

lodash 就是这样的一个库

lodash 是在 ES5 之前出现的库,使用的是 ES3 语法

通过module.rule.excludemodule.rule.include,排除或仅包含需要应用 loader 的场景

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /lodash/,
        use: "babel-loader",
      },
    ],
  },
};

如果暴力一点,甚至可以排除掉node_modules目录中的模块,或仅转换src目录的模块

js
//一般别的库都是做过兼容的,但是也不排除个别例外,使用这个还是要分析清楚的。
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        //或
        // include: /src/,
        use: "babel-loader",
      },
    ],
  },
};

这种做法是对 loader 的范围进行进一步的限制,和 noParse 不冲突,想想看,为什么不冲突 因为在解析文件前都会进行 loader 的使用,loader 的过程在前,所以不会冲突。

缓存 loader 的结果

我们可以基于一种假设:如果某个文件内容不变,经过相同的 loader 解析后,解析后的结果也不变

于是,可以将 loader 的解析结果保存下来,让后续的解析直接使用保存的结果

cache-loader可以实现这样的功能

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: ["cache-loader", ...loaders],
      },
    ],
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          // 使用cache-loader
          {
            loader: "cache-loader",
            options: {
              //设置缓存结果目录
              cacheDirectory: "./cache",
            },
          },
          "babel-loader",
        ],
      },
    ],
  },
};

有趣的是,cache-loader放到最前面,却能够决定后续的 loader 是否运行

实际上,loader 的运行过程中,还包含一个过程,即pitch

cache-loader还可以实现各自自定义的配置,具体方式见文档

我们之前学习的 loader 只是下面的一段,在此之前还有 pitch 阶段,loder 会先执行 pitch,当第一个 loader 的 pitch 有返回值,那么就直接跳过当前 loader,把结果传给前面的一个 loader

js
function loader(source) {
  return `new source`;
}
//添加pitch阶段
loader.pitch = function (filePath) {
  // 可返回可不返回
  // 如果返回,返回源代码
};

module.exports = loader;

为 loader 的运行开启多线程

thread-loader会开启一个线程池,线程池中包含适量的线程

它会把后续的 loader 放到线程池的线程中运行,以提高构建效率

由于后续的 loader 会放到新的线程中,所以,后续的 loader 不能:

  • 使用 webpack api 生成文件
  • 无法使用自定义的 plugin api (例如mini-css-extract-plugin 这个库的 plugin 为 webpack 的 api 做了扩展,之后这个库的 loader 又用到了扩展的这些 API)
  • 无法访问 webpack options

在实际的开发中,可以进行测试,来决定thread-loader放到什么位置

特别注意

  1. 开启和管理线程需要消耗时间,在小型项目中使用thread-loader反而会增加构建时间
  2. thread-loader尽量放在开销比较大的 loader 前,放在该 loader 之后的 loader 都会在新开的线程中执行。
js
module.exports = {
  mode: "development",
  devtool: "source-map",
  //
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          //   使用cache-loader
          {
            loader: "cache-loader",
            options: {
              cacheDirectory: "./cache",
            },
          },
          "thread-loader", //在这之后的loader都会被放在新的线程里
          "babel-loader",
        ],
      },
    ],
  },
};

MIT License