环境变量

Modern.js 提供了对环境变量的支持,包含内置的环境变量和自定义的环境变量。

内置的环境变量

ASSET_PREFIX

表示当前资源文件的路径前缀,是只读的的环境变量。

NODE_ENV

表示当前的执行环境,是只读的的环境变量,其值在不同的执行命令下具有不同的值:

  • production:执行 modern buildmodern serve 命令时的默认值。
  • test:执行 modern test 命令时的默认值。
  • development:执行 modern dev 命令时的默认值,同时也是其他所有情况下的默认值。

MODERN_ENV

手动设置当前的执行环境。除了上述 NODE_ENV 对应的值之外,这里支持自定义环境名称,例如 stagingboe 等。

TIP

MODERN_ENV 的优先级高于 NODE_ENV。

MODERN_TARGET

使用 @modern-js/runtime 时,Modern.js 会自动注入 MODERN_TARGET,用于区分 SSR 与 CSR 环境。

你可以在代码中通过 process.env.MODERN_TARGET 来判断环境,并执行相应的逻辑。

App.tsx
function App() {
  if (process.env.MODERN_TARGET === 'browser') {
    console.log(window.innerHeight);
  }
}

在开发环境构建完成后,可以看到 SSR 产物和 CSR 产物如下:

dist/bundles/main.js
// SSR 产物
function App() {
  if (false) {
  }
}
dist/static/main.js
// CSR 产物
function App() {
  if (true) {
    console.log(window.innerHeight);
  }
}

这种方式可以针对不同客户端提供不同的产物,保证代码体积最小化;也便于处理不同环境下代码中的一些副作用。

死代码移除

在生产环境,Terser 和 SWC 等代码压缩工具会分析代码,并将 dead code 移除,从而减少产物体积,这个过程被称为死代码移除(DCE)。

例如,上述 if (false) 语句包含的代码会被移除,而 if (true) 包含的代码将被保留。

如果你未按照上述写法来使用 process.env.MODERN_TARGET,代码压缩工具可能会无法分析出 dead code,从而导致产物体积增大。

自定义环境变量

环境变量支持通过 shell.env 文件两种方式指定。

通过 shell 指定

在命令前添加自定义环境变量:

REACT_APP_FOO=123 BAR=456 pnpm run dev

通过 .env 文件指定

在项目根目录创建 .env 文件,并添加自定义环境变量,这些环境变量会默认添加到启动项目的 Node.js 进程中,例如:

REACT_APP_FOO=123
BAR=456

.env 文件遵循以下规则加载规则:

  • .env:默认加载。
  • .env.{ MODERN_ENV | NODE_ENV }:针对具体环境设置环境变量,会覆盖 .env 中的设置。

当需要根据环境使用不同的配置时,可以把环境变量定义到对应环境名称的 .env 文件中,并在启动项目时手动设置执行环境。例如使用以下命令启动项目时,将会加载 .env.env.staging:

MODERN_ENV=staging pnpm run dev

使用环境变量

约定命名

在前端代码中可以直接使用 NODE_ENV 环境变量。另外,以 MODERN_ 开头的自定义环境变量,也可以在代码中直接使用。

例如:

if (process.env.NODE_ENV === 'development') {
  // do something
}

执行 pnpm run dev 命令之后可以看到如下构建产物:

if (true) {
  // do something
}

同样在自定义的 HTML 模板中,也可以直接使用这类环境变量。如 config/html/head.html:

<meta name="test" content="<process.env.NODE_ENV>">

任意命名

如果需要在代码中使用任意名称的环境变量,可以在 source.globalVars 配置指定, 例如:

modern.config.ts
export default defineConfig({
  source: {
    globalVars: {
      'process.env.VERSION': process.env.VERSION,
    }.
  },
});

此时,在代码中的 process.env.VERSION,将会被替换为环境变量中 VERSION 的值。

NOTE

source.globalVars 也支持将其他表达式或字符串替换为指定的值,不仅限于环境变量。

使用全局替换

除了环境变量,Modern.js 也支持将代码中的变量替换成其它值或者表达式,可以用于在代码逻辑中区分开发环境与生产环境等场景。

例如将代码中的 TWO 转换为 1 + 1 的表达式:

export default {
  source: {
    define: {
      TWO: '1 + 1',
    },
  },
};
const foo = TWO;

// ⬇️ Turn into being...
const foo = 1 + 1;

在大多数情况下,source.globalVars 已经能满足替换变量的需求。但 source.globalVars 传入的值都会默认被 JSON 序列化,因此无法做出像上面例子中 1 + 1 的替换,此时就需要使用 source.define