☰
Storybook viteFinal 配置实战:自定义 Vite 构建器的开发与生产配置
2026/11/5 11:48:37 网站建设 项目流程

Storybook viteFinal 配置实战:自定义 Vite 构建器的开发与生产配置

【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook

Storybook 官方文档将viteFinal定义为 Vite builder 场景下的配置入口函数,用于在 Storybook 的 Vite 构建管线中注入自定义配置。本篇以 main-config-vite-final.md 中提供的完整代码骨架为主体,结合 viteFinal API 参考、Vite 构建器指南以及@storybook/builder-vite的真实实现源码,讲解viteFinal的签名、调用时机、环境区分写法与底层原理。读完本文,你将能够在自己项目的.storybook/main.js|ts中写出类型安全、可区分开发/生产环境的 Vite 定制逻辑,并理解为何要用mergeConfig合并配置。

viteFinal 是什么:main 配置中的 Vite 定制钩子

当 Storybook 使用 Vite builder 打包组件与 stories 时(Vite 是当前 Storybook 的默认 builder,代码见 code/builders/builder-vite),框架已经为受支持的各类框架内置了一套 Vite 配置默认值,并将其与你项目中已有的vite.config.js|ts自动合并。官方推荐:能用 Vite 自身配置文件解决的问题,优先直接在 Vite 配置文件内完成;只有当 Storybook 场景需要额外定制时,才在 .storybook/main.js|ts 中通过viteFinal追加配置。

依据 viteFinal API 参考,其完整类型签名如下:

(config: Vite.InlineConfig, options: Options) => Vite.InlineConfig | Promise<Vite.InlineConfig> // Options 类型为 // { configType?: 'DEVELOPMENT' | 'PRODUCTION' }

viteFinal是一个异步函数,它接收 Vite builder 组装好的默认config对象,经过你的改造后返回(或异步返回)最终生效的配置。这种"接收默认配置→修改→返回"的设计,使它对已有的默认配置是追加式定制,而不是另起炉灶重写一套构建配置。

它在构建管线中的真实调用位置

从@storybook/builder-vite源码可以确认,viteFinal并非魔法钩子,而是通过 Storybook 的 preset 管线统一执行的:

  • 开发服务器:在 code/builders/builder-vite/src/vite-server.ts 中,创建 Vite dev server 前执行presets.apply('viteFinal', config, options);
  • 生产构建:在 code/builders/builder-vite/src/build.ts 中,调用 Vite 的build前执行同样的presets.apply('viteFinal', config, options);
  • 无头工具链:在 code/builders/builder-vite/src/change-detection-adapter/headless.ts 中,storybook tools这类无需 dev server 的场景同样按"先commonConfig(options, 'development')再套用viteFinal"的顺序组装配置。

也就是说,你在viteFinal中写下的逻辑,会同时作用于本地开发、storybook build生产构建以及依赖同一套配置解析的工具场景。测试代码 vite-config.test.ts 中configType: 'DEVELOPMENT'的用例也印证了configType这一参数的真实形态。

顺带一提,Storybook 的遥测模块会检测 main 配置中是否存在viteFinal并记录hasCustomVite元数据,相关实现见 code/core/src/telemetry/storybook-metadata.ts。

最小可用骨架:在 main 配置中声明 viteFinal

下面是与关联文档一致的完整最小骨架(.storybook/main.js),展示了viteFinal的标准用法:动态导入 Vite 的mergeConfig,用configType区分环境,最后合并并返回配置:

export default { // Replace your-framework with the framework you are using, e.g. react-vite, nextjs-vite, vue3-vite, etc. framework: '@storybook/your-framework', stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'], async viteFinal(config, { configType }) { const { mergeConfig } = await import('vite'); if (configType === 'DEVELOPMENT') { // Your development configuration goes here } if (configType === 'PRODUCTION') { // Your production configuration goes here. } return mergeConfig(config, { // Your environment configuration here }); }, };

其中三个要点:

  1. configType由框架注入,取值为'DEVELOPMENT'或'PRODUCTION',分别对应storybook dev与storybook build两个命令的运行环境(configType在 transform-iframe-html.ts 中同样被用于区分两种模式下的 HTML 模板处理);
  2. mergeConfig是从vite包动态导入的工具函数,它按 Vite 的语义深度合并配置对象,是官方示例中推荐的合并方式。直接用对象展开{ ...config, ...myConfig }会丢失嵌套键(如resolve.alias、plugins数组);
  3. framework需替换为实际安装的框架包名,例如@storybook/react-vite、@storybook/vue3-vite、@storybook/nextjs-vite、@storybook/web-components-vite等(对应仓库 code/frameworks 下各 Vite 系框架目录)。

TypeScript 版本

若项目使用 TypeScript,可将.storybook/main.js重命名为.storybook/main.ts,并引入对应框架的类型定义以获得完整的配置项类型推导与编译期校验:

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs-vite, vue3-vite, etc. import type { StorybookConfig } from '@storybook/your-framework'; const config: StorybookConfig = { framework: '@storybook/your-framework', stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'], async viteFinal(config, { configType }) { const { mergeConfig } = await import('vite'); if (configType === 'DEVELOPMENT') { // Your development configuration goes here } if (configType === 'PRODUCTION') { // Your production configuration goes here. } return mergeConfig(config, { // Your environment configuration here }); }, }; export default config;

CSF Next(实验性)写法:defineMain

Storybook 较新版本提供了实验性的 "CSF Next" 配置写法,通过从@storybook/<framework>/node导入defineMain来声明 main 配置。由于defineMain接收完整的泛型,configType等回调参数能够获得自动类型推断,无需手写StorybookConfig:

import { defineMain } from '@storybook/vue3-vite/node'; export default defineMain({ framework: '@storybook/vue3-vite', stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'], async viteFinal(config, { configType }) { const { mergeConfig } = await import('vite'); if (configType === 'DEVELOPMENT') { // Your development configuration goes here } if (configType === 'PRODUCTION') { // Your production configuration goes here. } return mergeConfig(config, { // Your environment configuration here }); }, });

对应不同框架时仅需替换包名与framework字段,例如@storybook/web-components-vite/node+'@storybook/web-components-vite',或 React 生态下的@storybook/react-vite/node。CSF Next 写法主要面向新项目与框架新版本,两种风格在功能上是等价的。

分环境定制:开发与生产的差异化配置

Options.configType的取值直接告诉你当前是哪种模式,配合 if 分支即可做到互不干扰的差异化定制,典型场景包括:

  • DEVELOPMENT:放宽性能限制、注入仅本地需要的 mock 插件、调整server相关配置以加速热更新;
  • PRODUCTION:压缩代码、剔除仅调试用的依赖、设置面向静态托管(如 Chromatic)的输出相关选项。

这与 viteFinal API 参考 中给出的 Options 类型{ configType?: 'DEVELOPMENT' | 'PRODUCTION' }完全对应。API 参考同时注明:Options还包含一些难以在文档中一一列出的其余字段,如需穷举可自行查看类型定义文件。

需要留意的是:环境定制不应违背"能不改就不改"的原则。Vite 构建器指南 明确指出 Vite 相比 Webpack 开箱即用能力更强(如样式加载多数项目无需配置),Webpack 迁移场景建议先以零 Storybook 专属 Vite 配置起步,再按实际需求逐项补充。

真实场景:alias、依赖预构建与配置合并

上文骨架只是框架,落到实际需要把viteFinal与mergeConfig用起来。以下是从 storybook-vite-builder-aliasing.md 摘取的代表性示例——将storybook-dark-mode这类依赖加入 Vite 的optimizeDeps.include预构建白名单,以规避暗色主题等依赖在开发模式下的预构建报错:

import type { StorybookConfig } from '@storybook/your-framework'; const config: StorybookConfig = { framework: '@storybook/your-framework', stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'], core: { builder: '@storybook/builder-vite', }, async viteFinal(config) { // Merge custom configuration into the default config const { mergeConfig } = await import('vite'); return mergeConfig(config, { // Add dependencies to pre-optimization optimizeDeps: { include: ['storybook-dark-mode'], }, }); }, }; export default config;

当项目以 Vite 应用为基底时,viteFinal常与resolve.alias等 Vite 配置联动:先在应用的vite.config.ts中统一维护路径别名,再在viteFinal中按需向 Storybook 的 config 追加(官方建议一切能放vite.config的配置优先放在那里,因为 Storybook 加载时会自动合并它)。如果明确用不到vite.config之外的定制,viteFinal也可以完全省略。

相关配套能力:viteConfigPath 与 configLoader

viteFinal定制的是"配置内容",而以下两个 builder 选项定制的则是"配置来源与加载方式",三者常被放在一起讨论:

  • viteConfigPath:默认 builder 会在 Storybook 项目根目录查找 Vite 配置文件,若希望读取其他位置的配置文件(或完全禁用自动加载——只需把viteConfigPath指向一个不存在的文件),可在core.builder的对象形态中声明,完整示例见 main-config-builder-custom-config.md:
export default { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], core: { builder: { name: '@storybook/builder-vite', options: { viteConfigPath: '../customVite.config.js', }, }, }, };
  • configLoader:通过 builder options 或等价命令行参数--configLoader传入,交给 Vite 决定配置文件用何种方式加载(如 bundle 或 native),详见 Vite 构建器指南 中 "Set configLoader for vite config" 一节与 main-config-builder-configLoader.md。Vite 支持的合法取值需查阅 Vite 官方配置文档。

底层机制:commonConfig → viteFinal 的两段式组装

结合源码可以看到,viteFinal之所以"能拿到一份可继续加工的默认配置",是因为 builder 内部采用了清晰的两段式组装:

  1. commonConfig(options, mode)先生成包含框架默认值、Stories 入口、插件等内容的基线配置(开发模式下 mode 为'development',生产模式对应'build',见 vite-config.test.ts 对commonConfigMock的断言);
  2. 随后presets.apply('viteFinal', config, options)把你的定制逻辑串接到这条 preset 链的末端。

因此你在viteFinal中拿到的config已经是"Storybook 视角的完整基线",mergeConfig负责把新增项正确地融合进去,最终交回 Vite。这套机制也解释了为何 Vite 构建器指南 在"工作目录未被正确识别"等故障排查场景中,建议直接用viteFinal覆盖默认行为——例如当server.fs.strict与配置文件根目录的默认推断与你项目实际布局冲突时,viteFinal就是唯一且对口的调整点。

小结与最佳实践

围绕viteFinal,建议遵循以下实践:

  1. 默认优先:通用 Vite 配置尽量放在vite.config.js|ts,Storybook 会自动合并;viteFinal只放 Storybook 专属定制;
  2. 用 mergeConfig 合并:官方示例统一从vite动态导入mergeConfig,避免浅拷贝覆盖嵌套配置;
  3. 按 configType 分流:用configType === 'DEVELOPMENT' / 'PRODUCTION'区分 dev 与 build 环境;
  4. 善用类型:TS 项目使用StorybookConfig标注,或尝试defineMain(CSF Next)获得推断;
  5. 了解作用域:viteFinal的改动会同时影响 dev server、生产构建与无头工具场景,改动面较大,需谨慎验证。

延伸阅读:viteFinal API 参考、Vite 构建器指南、main.js|ts 配置总览。

【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询