首页
/ Storybook 环境变量配置指南:在 main.js / main.ts 中通过 env 字段注入自定义环境变量

Storybook 环境变量配置指南:在 main.js / main.ts 中通过 env 字段注入自定义环境变量

2026-09-07 12:35:00作者:戚魁泉Nursing

本篇以 Storybook 主配置文件(.storybook/main.js|ts)中的 env 配置项为主线,讲解如何在不依赖 .env 文件的前提下,直接在 Storybook 配置里合并、新增自定义环境变量,并说明这些变量在 story、preview-head、Vite/Webpack 构建链路中的传递原理。读完你将掌握 env 字段的完整签名、CSF 3 与 CSF Next 两种配置写法的落地姿势,以及 STORYBOOK_ 前缀变量、.env 文件与 env 函数三者之间的合并优先级。

一、env 是什么:定义于主配置文件的 API

env 是 Storybook 主配置文件(main.js|ts 配置)中的一个配置字段,其官方 API 参考位于 main-config-env.mdx。它的类型签名是:

(config: { [key: string]: string }) => { [key: string]: string }

也就是说,env 接收一个函数,入参 config 是一个字符串键值对对象,返回值同样是一个字符串键值对对象。它的作用是:定义自定义的 Storybook 环境变量——在你希望为不同组件、不同 story 提供差异化 API URL 或功能开关时,可以在这个函数中一次性声明(相关内容见官方环境变量指南)。

一个关键语义是注释里反复强调的那句话:

👇 config 参数包含所有其他已有的环境变量——无论是配置在 .env 文件中,还是通过命令行配置的。

这决定了它的本质是一个合并函数:入参里已经躺着 Storybook 从 .env 文件和命令行里收集到的现有环境变量,你要做的是在返回的对象中把它们展开并追加自定义项。

二、基础用法:CSF 3 写法下的完整配置示例

下面分别给出 .storybook/main.js.storybook/main.ts 两种格式的完整示例。使用时请把 @storybook/your-framework 替换为你实际使用的框架,例如 react-vitenextjsvue3-vite 等。

JavaScript(.storybook/main.js):

export default {
  // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
};

TypeScript(.storybook/main.ts):

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';

const config: StorybookConfig = {
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
};

export default config;

两点值得注意:

  1. 务必先展开 ...config:入参 config 携带了 .env 与命令行注入的全部既有变量。如果不先展开,返回的对象将"淹没"这些既有值,可能导致原有变量(包括一些构建依赖的系统变量)丢失。
  2. 返回值中新增自定义键:上例的 EXAMPLE_VAR 就是你在配置层面自定义的环境变量,加载完成后可在 story 代码里读取(见第四节)。

三、进阶写法:CSF Next(🧪 实验性)中的 defineMain 形式

源码仓库同时提供了基于实验性 CSF Next API defineMain 的写法。与 CSF 3 的区别在于:不再 export default 一个普通对象,而是从 @storybook/<框架>/node 导出 defineMain 并包裹整个配置。当前仓库各渲染器均支持该模式,下面是针对不同框架的完整示例。

React(TypeScript,.storybook/main.ts):

// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

React(JavaScript,.storybook/main.js):

// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

Vue 3(TypeScript,.storybook/main.ts)——需从 vue3-vite 框架入口导入:

import { defineMain } from '@storybook/vue3-vite/node';

export default defineMain({
  framework: '@storybook/vue3-vite',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

Vue 3(JavaScript,.storybook/main.js):

import { defineMain } from '@storybook/vue3-vite/node';

export default defineMain({
  framework: '@storybook/vue3-vite',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

Angular(TypeScript,.storybook/main.ts)——框架名为 @storybook/angular

import { defineMain } from '@storybook/angular/node';

export default defineMain({
  framework: '@storybook/angular',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

Web Components(TypeScript,.storybook/main.ts):

import { defineMain } from '@storybook/web-components-vite/node';

export default defineMain({
  framework: '@storybook/web-components-vite',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

Web Components(JavaScript,.storybook/main.js):

import { defineMain } from '@storybook/web-components-vite/node';

export default defineMain({
  framework: '@storybook/web-components-vite',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  /*
   * 👇 The `config` argument contains all the other existing environment variables.
   * Either configured in an `.env` file or configured on the command line.
   */
  env: (config) => ({
    ...config,
    EXAMPLE_VAR: 'An environment variable configured in Storybook',
  }),
});

可以发现,无论哪个框架,env 字段的写法完全一致——这正是它作为主配置中"与框架无关"能力的设计意图。CSF Next 变体当前以 🧪 标识,属于仍在演进的实验性 API,生产项目中若追求稳定,建议采用第二节的 CSF 3 写法。

四、在 story 与组件中读取自定义变量

当 Storybook 启动加载后,你在 env 函数里声明的变量(如 EXAMPLE_VAR)即可在 stories 中读取,用法与使用 .env 文件时几乎一致。仓库配套示例 my-component-env-var-config.md 给出了各框架下通过 process.env.EXAMPLE_VAR 注入组件 args 的写法,例如:

// MyComponent.stories.tsx (React,CSF 3)
import type { Meta, StoryObj } from '@storybook/react-vite';

import { MyComponent } from './MyComponent';

const meta = {
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Basic: Story = {
  args: {
    exampleProp: process.env.EXAMPLE_VAR,
  },
};

读取侧的注意事项取决于你使用的构建器(详见 Vite builder 说明):

  • Webpack 系构建器:以 process.env.EXAMPLE_VAR 方式访问;
  • Vite 构建器:Vite 默认不输出 process.env 这类 Node.js 全局对象,需改用 import.meta.env(同时支持 STORYBOOK_VITE_ 前缀变量),仓库配套示例 my-component-vite-env-variables.md 演示了这一点。

如果你使用的是 Webpack 系框架(例如 Angular + Webpack)且出现 Can't find variable: process 之类的运行时错误,通常意味着某几个 STORYBOOK_ 前缀变量缺失或未正确配置——需要确认它们已通过 .env 文件、命令行参数或主配置的 env 字段注入,并给出默认值兜底。

五、与 .env 文件、命令行注入的关系

env 字段并非孤立存在,它处于 Storybook 环境变量体系的一个交汇点上。整个体系可以归纳为三层:

注入途径 示例 说明
命令行前缀变量 STORYBOOK_THEME=red npm run storybook STORYBOOK_ 前缀变量会进入前端运行时可访问的环境
.env / .env.development / .env.production 文件 STORYBOOK_DATA_KEY=12345 按模式拆分:开发与生产构建可应用不同值
主配置 env 字段 env: (config) => ({ ...config, EXAMPLE_VAR: '...' }) 在配置层合并/新增自定义变量

关于三层之间的关系,结合本节开头引用的官方注释与环境变量文档,可以明确两件事:

  1. env 函数入参 config 即包含前两层收集到的全部既有环境变量,因此该函数天然具备"在保留既有变量基础上追加变量"的能力,这正是示例中 ...config 展开的意义;
  2. .env 中若使用 STORYBOOK_DATA_KEY=12345,你在 stories 甚至组件代码内都可直接读取——这与 env 函数新增变量后的读取体验一致。两者互补:需要跟随运行模式变化的变量适合放 .env,纯粹由 Storybook 配置衍生的常量适合放 env 函数。

此外,使用 build-storybook 构建静态站点时也可传入这些环境变量,它们会被硬编码进静态产物——因此每个部署环境的差异值必须在构建时确定。

六、源码原理:env 值如何流入预览与构建

要理解 env 字段为什么能"新增任意键"(而不像 .env 那样只放行 STORYBOOK_ 前缀),需要进入源码看合并链路。

6.1 默认 env preset:收集 dotenv 与进程变量

核心源码位于 envs.ts。其中 loadEnvs() 通过 lazy-universal-dotenv 读取 .env 文件,并拼上 process.env

// code/core/src/common/utils/envs.ts(节选,非逐字)
const baseEnv = {
  NODE_ENV: process.env.NODE_ENV || defaultNodeEnv,
  STORYBOOK: process.env['STORYBOOK'] || 'true',
  PUBLIC_URL: options.production ? '.' : '',
  // ...
};
const envEntries = Object.fromEntries(
  Object.entries({ ...process.env, ...dotenv.raw })
    .filter(([name]) => /^STORYBOOK_/.test(name))
);

从源码结构看,默认收集链路对进程变量与 .env 变量做了一次仅放行 STORYBOOK_ 前缀的过滤(/^STORYBOOK_/),随后产出供构建使用的 stringified(JSON 字符串化)与 raw 两组值,并提供了把 raw 转成 process.env.X 形式键的 stringifyProcessEnvs——这正是 Webpack 环境注入时的形状。

6.2 env preset 与用户 env 字段的叠加

common-preset.ts 中,默认的 env preset 直接返回 loadEnvs({ production: true })raw

export const env = async () => {
  const { raw } = await loadEnvs({ production: true });
  return raw;
};

而你在 main.js|ts 中提供的 env 函数,会以 preset 叠加(presets.apply)的方式作用在默认结果之上——返回对象中展开的 ...config 保留了默认 preset 的全部既有变量,新增的 EXAMPLE_VAR 则被并入同一份键值表。

6.3 preview-head / preview-body 的 %STORYBOOK_X% 插值

同一文件中,previewHeadpreviewBody 都会执行 presets.apply<Record<string, string>>('env'),把合并后的变量集交给模板渲染:

export const previewHead = async (base: any, { configDir, presets }: Options) => {
  const interpolations = await presets.apply<Record<string, string>>('env');
  return getPreviewHeadTemplate(configDir, interpolations);
};

这解释了自定义 <head> / <body>(即 preview-head.htmlpreview-body.html)中的 %STORYBOOK_X% 替换机制:%STORYBOOK_THEME% 会被替换成变量实际值,例如前面命令行示例中的 red。若把变量用作 HTML 属性值,需自行加引号,例如 <link rel="stylesheet" href="%STORYBOOK_STYLE_URL%" />,因为该值会被原样插入

综上,env 字段产出的变量会同时流向三条通道:Webpack/Vite 构建注入(story 内 process.env/import.meta.env 读取)、preview-head.html/preview-body.html%STORYBOOK_X% 插值、以及浏览器中的常量替换。

七、安全红线与实操建议

  1. 绝对不要存放密钥等敏感信息:环境变量会被嵌入构建产物,任何查看你静态文件的人都能读到。官方环境变量文档对此给出了明确警告。API Key、私密 token 一律走服务端环境,而不是 Storybook 的环境变量。
  2. 保留展开,避免覆盖:写 env 函数时务必保留 ...config,否则会丢弃 .env 与命令行里已有的变量。
  3. 读取端注意构建器差异:Webpack 用 process.env.X,Vite 用 import.meta.env.XVite builder)。
  4. 模式化配置:需要按开发/生产区分的值优先使用 .env.development.env.production 文件;需要随 Storybook 配置本身变化的值放进 env 字段。
  5. 选择稳定 API:生产项目优先使用 CSF 3 的普通导出写法;实验性的 CSF Next defineMain 形式(仓库中以 🧪 标注)适合在升级测试环境中先行验证。

八、延伸阅读

登录后查看全文
热门项目推荐
相关项目推荐