close
CC 4.0 协议

本节内容派生于以下链接指向的内容 ,并遵守 CC BY 4.0 许可证的规定。

以下内容如果没有特殊声明,可以认为都是基于原内容的修改和删减后的结果。

DefinePlugin

DefinePlugin 在编译时将代码中的变量替换为其他值或表达式。这对于允许开发构建和生产构建之间的不同行为非常有用。如果你在开发构建中执行日志记录,但在生产构建中则不执行,你可能会使用一个全局常量来决定是否进行日志记录。这正是 DefinePlugin 的用武之地,为开发和生产构建设置一成不变的规则。

new rspack.DefinePlugin({
  // 定义...
});

选项

  • 类型:
type CodeValue = RecursiveArrayOrRecord<CodeValuePrimitive>;
type CodeValuePrimitive =
  null | undefined | RegExp | Function | string | number | boolean | bigint;
type RecursiveArrayOrRecord<T> =
  | { [index: string]: RecursiveArrayOrRecord<T> }
  | Array<RecursiveArrayOrRecord<T>>
  | T;

type DefinePluginOptions = Record<string, CodeValue>;

示例

基本使用

传入 DefinePlugin 的每个键都是一个标识符或多个标识符用 . 连接。

  • 如果值是一个字符串,它将被用作代码片段。
  • 如果值不是字符串,它会被转化为字符串(包括函数)。
  • 如果值是一个对象,所有键都以相同的方式定义。
  • 如果你在键前加上 typeof,它只在 typeof 调用中被定义。

这些值将被内联到代码中,允许在压缩阶段删除掉那些冗余的条件判断。

new rspack.DefinePlugin({
  PRODUCTION: JSON.stringify(true),
  VERSION: JSON.stringify('5fa3b9'),
  BROWSER_SUPPORTS_HTML5: true,
  TWO: '1+1',
  'typeof window': JSON.stringify('object'),
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
});
console.log('Running App version ' + VERSION);
if (!BROWSER_SUPPORTS_HTML5) require('html5shiv');
警告

在为 process 定义值时,推荐使用 'process.env.NODE_ENV': JSON.stringify('production') 而不是 process: { env: { NODE_ENV: JSON.stringify('production') } }。使用后者会重写 process 对象,这可能会破坏对某些模块的兼容性,这些模块期望 process 对象上的其他值被定义。

提示

请注意,由于插件直接执行文本替换,因此提供给它的值必须包含字符串内部的实际引号。通常,这是通过使用交替引号,如 '"production"',或使用 JSON.stringify('production') 来完成。

if (!PRODUCTION) {
  console.log('Debug info');
}

if (PRODUCTION) {
  console.log('Production log');
}

通过 Rspack,但不进行压缩后的结果为:

if (!true) {
  console.log('Debug info');
}
if (true) {
  console.log('Production log');
}

在压缩后的结果为:

console.log('Production log');

特性标志

使用特性标志在生产/开发构建中启用/禁用功能。

new rspack.DefinePlugin({
  NICE_FEATURE: JSON.stringify(true),
  EXPERIMENTAL_FEATURE: JSON.stringify(false),
});

服务 URL

在生产/开发构建中使用不同的服务 URL:

new rspack.DefinePlugin({
  SERVICE_URL: JSON.stringify('https://dev.example.com'),
});

使用 JSON.stringify

当你希望注入的是字面量值,而不是代码片段时,应使用 JSON.stringify

常见场景包括:

  • 注入字符串常量,例如环境名、版本号、接口地址。
  • 注入对象或数组,并在业务代码中直接读取其内容。
  • 注入 process.env.* 这类来自环境变量的字符串值。

这是因为 DefinePlugin 中的字符串会被直接当作代码插入,而不会自动补上引号。

new rspack.DefinePlugin({
  MODE: 'production',
  MODE_STRINGIFIED: JSON.stringify('production'),
});
const mode = MODE;
const modeStringified = MODE_STRINGIFIED;

// 会变成
const mode = production;
const modeStringified = 'production';

上面的 MODE 会被当作标识符 production,而不是字符串 'production',通常会导致运行时错误。MODE_STRINGIFIED 则会被替换为合法的字符串字面量,是更安全的写法。

如果需要注入对象或数组,也建议使用 JSON.stringify

new rspack.DefinePlugin({
  APP_INFO: JSON.stringify({
    name: 'demo',
    features: ['a', 'b'],
  }),
});

这样生成的 APP_INFO 会是一个可直接使用的对象字面量。否则,像 APP_INFO: { ... } 这样的写法会被 DefinePlugin 当作嵌套定义配置递归处理,而不是按对象字面量注入到业务代码中。

import.meta.env

启用 experiments.env 后,可以通过 DefinePlugin 定义 import.meta.env

rspack.config.mjs
export default {
  experiments: {
    env: true,
  },
  plugins: [
    new rspack.DefinePlugin({
      'import.meta.env': {
        MODE: JSON.stringify('production'),
        API_URL: JSON.stringify('https://example.com'),
      },
    }),
  ],
};

只访问单个字段时,Rspack 会直接内联使用到的值:

src/index.js
console.log(import.meta.env.MODE);
console.log(import.meta.env.NOT_EXIST);

上面的代码大致等价于:

dist/main.js
console.log('production');
console.log(undefined);

API_URL 没有被访问,因此不会出现在这段生成代码中。

读取整个 import.meta.env 时,Rspack 会生成包含所有已定义字段的对象:

src/index.js
const env = import.meta.env;

console.log(typeof env); // 'object'
console.log(Boolean(env)); // true
console.log(env.MODE); // 'production'

上面的代码大致等价于:

dist/main.js
const env = {
  MODE: 'production',
  API_URL: 'https://example.com',
};

console.log(typeof env);
console.log(Boolean(env));
console.log(env.MODE);

启用 experiments.env 时,EnvironmentPluginNODE_ENV 以及 DefinePlugin 中的 import.meta.env.* 键会被收集到 import.meta.env