运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

定义并替换静态全局变量和常量

--define 标志用于声明可静态分析的常量和全局变量。它会将 JavaScript 或 TypeScript 文件中标识符或属性的所有用法替换为常量值,并且在运行时和 bun build 中均可使用。它类似于 C/C++ 中的 #define,但适用于 JavaScript。

terminal
bun --define process.env.NODE_ENV="'production'" src/index.ts # 运行时
bun build --define process.env.NODE_ENV="'production'" src/index.ts # 构建时

Bun 会利用这些静态已知值来消除无效代码并执行其他优化。

TypeScript
if (process.env.NODE_ENV === "production") {
  console.log("生产模式");
} else {
  console.log("开发模式");
}

在代码到达 JavaScript 引擎之前,Bun 会将 process.env.NODE_ENV 替换为 "production"。

TypeScript
if ("production" === "production") { // [!code ++]
  console.log("生产模式");
} else {
  console.log("开发模式");
}

Bun 的优化型转译器也会执行基本的常量折叠。

由于 "production" === "production" 始终为 true,Bun 会将整个表达式替换为 true 值,并移除不可达的 else 分支。

TypeScript
if (true) { // [!code ++]
  console.log("生产模式");
}

若还要将周围的 if 框架折叠为以下输出,请传入 --minify-syntax(--minify 也会启用该选项):

terminal
bun build --define process.env.NODE_ENV="'production'" --minify-syntax src/index.ts
TypeScript
console.log("生产模式");

支持哪些类型的值?

值可以是字符串、标识符、属性或 JSON。

替换全局标识符

将所有 window 的使用替换为 undefined:

shell
bun --define window="undefined" src/index.ts

此替换适用于服务器端渲染(SSR),或确保代码不依赖 window 对象。

JavaScript
if (typeof window !== "undefined") {
  console.log("客户端代码");
} else {
  console.log("服务器端代码");
}

该值也可以是另一个标识符。例如,将所有 global 的使用替换为 globalThis:

shell
bun --define global="globalThis" src/index.ts

global 是 Node.js 中的全局对象,但在 Web 浏览器中并不是,因此此替换可以修复假设 global 可用的代码。

使用 JSON 替换值

--define 也可以将值替换为 JSON 对象和数组。

将所有 AWS 的使用替换为 JSON 对象 {"ACCESS_KEY":"abc","SECRET_KEY":"def"}:

shell
# JSON
bun --define AWS='{"ACCESS_KEY":"abc","SECRET_KEY":"def"}' src/index.ts

Bun 会将这些内容转换为等效的 JavaScript 代码。

从:

TypeScript
console.log(AWS.ACCESS_KEY); // => "abc"

变成:

TypeScript
console.log({ ACCESS_KEY: "abc", SECRET_KEY: "def" }.ACCESS_KEY);

使用其他属性替换值

你也可以传递属性给 --define 标志。

例如,将所有 console.write 的使用替换为 console.log:

shell
bun --define console.write=console.log src/index.ts

这会将以下输入转换:

TypeScript
console.write("Hello, world!");

变成以下输出:

TypeScript
console.log("Hello, world!");

这和设置变量有什么不同?

你也可以在代码中将 process.env.NODE_ENV 设置为 "production",但这对死代码消除没有帮助。在 JavaScript 中,属性访问可能产生副作用。Getter 和 setter 可以是函数,甚至可以动态定义(由于原型链和 Proxy)。即使你将 process.env.NODE_ENV 设置为 "production",静态分析工具也无法假定它在下一行仍然是 "production"。

这和查找替换或字符串替换有什么不同?

--define 标志操作的是 AST(抽象语法树),而不是文本。替换发生在转译过程中,因此可以参与死代码消除等优化。

字符串替换工具往往有转义问题,并且可能替换到代码中不应替换的部分。