创见博客
webpack include 与 exclude:限定 loader 的处理范围
七崽爱吃小饼干2026/09/16阅读 0专栏 webpack构建高级

在配置 module.rules 时,很多人只关注 test(匹配哪些文件),却忽略了 include / exclude(限定在哪些范围内处理)。结果一个 babel-loader 把所有 node_modules 也跑了一遍,构建慢得离谱。本文讲清这两个字段的作用、区别和常见用法。

一、为什么需要 include / exclude

1. loader 默认「有 test 就全跑」

test 只负责筛选文件类型,一旦匹配,loader 就会对所有符合该类型的文件生效——包括 node_modules 里成千上万的第三方文件。

js
{
  test: /\.m?js$/,
  use: 'babel-loader',
}

这条配置看起来没问题,实际上会对 node_modules 下的每一个 JS 文件执行 Babel。后果是:

  • 构建极慢:第三方包数量庞大,逐個转译耗时惊人。
  • 完全没必要:npm 上的包通常已经发布为编译后的产物,不需要再次转译。
  • 可能出错:重复编译有时会导致奇怪的行为。

正确做法是限定范围:

js
{
  test: /\.m?js$/,
  exclude: /node_modules/,
  use: 'babel-loader',
}

2. 有时还需要「只处理某些目录」

反过来,有时希望 loader 只作用于特定目录(如只处理 src),避免误伤项目里其它位置的文件。这时用:

js
{
  test: /\.m?js$/,
  include: path.resolve(__dirname, 'src'),
  use: 'babel-loader',
}

二、include 与 exclude 是什么

它们都是 rule 上的过滤条件,和 test 一起决定一条规则是否对某个文件生效。规则匹配的完整语义是:

命中该规则 = 满足 test(若配置) 且 满足 include(若配置) 且 不满足 exclude(若配置)

换句话说:

  • include:白名单——只处理这些文件/目录。
  • exclude:黑名单——排除这些文件/目录。

两者作用相反,可以只写其中一个,也可以同时写(文件必须「在 include 内」且「不在 exclude 内」)。

三者的取值类型一致:

类型示例
字符串(必须是绝对路径)path.resolve(__dirname, 'src')
正则/node_modules/、/\.test\.js$/
数组(任一匹配即可)[path.resolve(__dirname, 'src'), path.resolve(__dirname, 'packages')]
函数(返回布尔值)(resourcePath) => !resourcePath.includes('vendor')

三、怎么用

1. 排除 node_modules(最常见)

js
{
  test: /\.m?js$/,
  exclude: /node_modules/,
  use: 'babel-loader',
}

2. 白名单式:只处理 src

js
const path = require('path');

{
  test: /\.m?js$/,
  include: path.resolve(__dirname, 'src'),
  use: 'babel-loader',
}

注意:字符串类型必须是绝对路径。直接写 'src' 是不生效的,因为 webpack 匹配的是文件的绝对路径。

3. include 和 exclude 怎么选

includeexclude
语义白名单,只处理列出的黑名单,排除列出的
范围控制更严格,误伤更少更宽松,容易漏网
适用场景明确知道要处理哪些目录主要想排除 node_modules

实践建议:能用 include 就用 include,范围更明确、性能更可控;如果只是不想动 node_modules,exclude: /node_modules/ 更省事。

4. 数组:指定多个目录

js
{
  test: /\.m?js$/,
  include: [
    path.resolve(__dirname, 'src'),
    path.resolve(__dirname, 'packages'),
  ],
  use: 'babel-loader',
}

5. 函数:更灵活的判断

js
{
  test: /\.m?js$/,
  include: (resourcePath) => !resourcePath.includes('node_modules'),
  use: 'babel-loader',
}

6. 排除特定文件,但保留目录

例如排除所有 .test.js 测试文件:

js
{
  test: /\.m?js$/,
  exclude: /\.test\.js$/,
  use: 'babel-loader',
}

7. 同时使用:include 为主,exclude 补充

js
{
  test: /\.m?js$/,
  include: path.resolve(__dirname, 'src'),
  exclude: path.resolve(__dirname, 'src/legacy'),
  use: 'babel-loader',
}

四、进阶:条件组合

rule 的条件对象支持 and / or / not 做组合,比单纯 include/exclude 更灵活:

js
{
  test: /\.js$/,
  // 排除 node_modules 中的测试文件
  not: [/node_modules/],
  // 或者用 and / or 组织多个条件
  // and: [/src/, /\.js$/],
}

以及可以用 resourceQuery、issuer 等更细的条件:

js
{
  test: /\.css$/i,
  include: path.resolve(__dirname, 'src'),
  exclude: /node_modules/,
  use: ['style-loader', 'css-loader'],
}

五、常见坑

  1. include 写了相对路径:字符串必须是绝对路径,否则永远匹配不到。用 path.resolve(__dirname, 'xxx') 最稳妥。
  2. 只写 exclude 却忘了根路径:exclude: /node_modules/ 是正则,会匹配路径中任意位置的 node_modules,一般没问题;但如果项目目录名里恰好含 node_modules,可能误伤。
  3. 有些 node_modules 确实需要编译:某些库发布的是未转译的 ES6+ 或 TS 源码。这时不能简单排除整个 node_modules,需要把它们放回白名单:
js
{
  test: /\.m?js$/,
  // 排除 node_modules,但保留 package-a、package-b
  exclude: /node_modules\/(?!(package-a|package-b)\/)/,
  use: 'babel-loader',
}
  1. 和 oneOf 一起用时位置要对:include / exclude 应写在各条规则内部;需要叠加的规则(如 ESLint)通常放在 oneOf 外面。

六、完整配置示例

js
const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        test: /\.m?jsx?$/,
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: { cacheDirectory: true },
        },
      },
      {
        test: /\.tsx?$/,
        include: path.resolve(__dirname, 'src'),
        use: 'ts-loader',
      },
      {
        test: /\.css$/i,
        include: path.resolve(__dirname, 'src'),
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(png|jpe?g|gif|svg)$/i,
        include: path.resolve(__dirname, 'src/assets'),
        type: 'asset',
        generator: { filename: 'images/[hash][ext][query]' },
      },
    ],
  },
};

小结

字段作用记忆方式
test按文件类型筛选匹配「是什么文件」
include白名单,限定处理范围只处理「这些」
exclude黑名单,排除特定范围不处理「这些」

一句话:test 决定处理什么类型,include / exclude 决定处理什么范围。 最常用的组合就是 exclude: /node_modules/;想让范围更明确、构建更可控,就用 include 指向源码目录。用好这两个字段,能避免大量无谓的 loader 执行,是构建性能优化的第一步。

评论
0/100