Storybook 主配置之 `core.builder`:自定义 Vite/Webpack 构建器与 `viteConfigPath` 实战指南
Storybook 主配置之core.builder自定义 Vite/Webpack 构建器与viteConfigPath实战指南【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybookcore.builder是 Storybookmain.js|ts配置中用于显式指定底层构建器Builder的字段。本文围绕.storybook/main中core.builder的两种写法字符串简写与{ name, options }对象形式展开结合 Storybook 仓库中 builder-vite 与 core 的类型与加载源码讲清楚构建器如何被选中、viteConfigPath到底相对于哪个目录解析以及在新 Framework API 下应优先使用framework.options.builder的原因。读完你可以在单仓、Monorepo、多构建器并存等场景中准确配置 Vite/Webpack 构建器并规避路径解析陷阱。一、core.builder在main.js|ts配置中的定位Storybook 的main配置.storybook/main.js或.storybook/main.ts由多个顶层字段组成其中core字段用于配置 Storybook 内部能力。在 main-config-core.mdx 中core的完整类型如下{ allowedHosts?: string[] | true; builder?: string | { name: string; options?: BuilderOptions }; channelOptions?: ChannelOptions; crossOriginIsolated?: boolean; disableProjectJson?: boolean; disableTelemetry?: boolean; disableWebpackDefaults?: boolean; disableWhatsNewNotifications?: boolean; enableCrashReports?: boolean; renderer?: RendererName; }其中builder字段负责选择 Storybook 用于构建预览页面的打包器——Vite 或 Webpack。与这一公开类型对应仓库内 core-common.ts 中的CoreConfig也给出了相同结构export interface CoreConfig { builder?: | BuilderName | { name: BuilderName; options?: Recordstring, any; }; renderer?: RendererName; disableWebpackDefaults?: boolean; // ... }BuilderName的定义同时兼容短名称与包名两种写法export type BuilderName webpack5 | storybook/builder-webpack5 | string;从源码结构看Storybook 的 core 层对 builder 采取鸭子类型接入只要name能被解析为可加载的 builder 包即可因此core.builder.name既可以是官方内置的storybook/builder-vite、storybook/builder-webpack5也可以是自定义第三方 builder 的包名。二、core.builder的两种配置形态core.builder支持字符串简写与对象形式两种写法// 形态一字符串简写 builder?: storybook/builder-vite | storybook/builder-webpack5 // 形态二对象形式可携带 options builder?: { name: storybook/builder-vite | storybook/builder-webpack5; options?: BuilderOptions; }字符串简写适用于只需切换 builder、不需要传任何构建器选项的场景对象形式的options会透传给 builder 包由具体 builder 自行解释。例如 builder-vite 会读取其中的viteConfigPath、configLoaderbuilder-webpack5 则会消费自己的构建缓存、懒编译等选项以各 builder 包实际实现为准。典型配置取自官方 Snippet官方对该字段提供的标准示例片段见 main-config-core-builder.md以下按渲染器与配置风格完整列出。CSF 3 风格的.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)], core: { builder: { name: storybook/builder-vite, options: { viteConfigPath: ../../../vite.config.js, }, }, }, };CSF 3 风格的.storybook/main.ts带StorybookConfig类型// 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 { stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], framework: storybook/your-framework, core: { builder: { name: storybook/builder-vite, options: { viteConfigPath: ../../../vite.config.js, }, }, }, }; export default config;CSF Next风格的 React 项目.storybook/main.ts使用defineMain// 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({ stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], framework: storybook/your-framework, core: { builder: { name: storybook/builder-vite, options: { viteConfigPath: ../../../vite.config.js, }, }, }, });CSF Next 同时也保留 JS 形态从storybook/your-framework/node导入defineMain。对 Vue 3 与 Web ComponentsdefineMain的导入来源分别为storybook/vue3-vite/node与storybook/web-components-vite/node配置体完全一致// .storybook/main.tsVue 3 import { defineMain } from storybook/vue3-vite/node; export default defineMain({ framework: storybook/vue3-vite, stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], core: { builder: { name: storybook/builder-vite, options: { viteConfigPath: ../../../vite.config.js, }, }, }, });// .storybook/main.tsWeb Components 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)], core: { builder: { name: storybook/builder-vite, options: { viteConfigPath: ../../../vite.config.js, }, }, }, });提示示例中的framework: storybook/your-framework是占位符需替换为你实际使用的框架包名如storybook/react-vite、storybook/nextjs、storybook/vue3-vite。若框架已默认绑定 builder如 react-vite 框架自带 builder-vite绝大多数情况下无需再写core.builder只有当你的框架并未内置 builder或需要显式覆盖构建器时才需要该字段。三、源码级原理builder-vite 如何消费core.builder.optionscore.builder.options中的内容最终会被 builder 包通过内部getBuilderOptions取回。以 builder-vite 为例其类型定义位于 builder-vite/src/types.tsexport type BuilderOptions { /** Path to vite.config file, relative to process.cwd(). */ viteConfigPath?: string; /** * How Vite loads the config file. Equivalent to Vites --configLoader CLI flag and the * configLoader option of loadConfigFromFile. * * Requires Vite 6.1.0 or higher. On older Vite versions this option is silently ignored. */ configLoader?: bundle | runner | native; };关键事实一viteConfigPath是相对process.cwd()的路径类型注释明确说明viteConfigPath是**相对于进程工作目录project root**的路径而非相对于.storybook/目录。这一点在加载逻辑中可得到验证。builder-vite 的 vite-config.ts 在组装公共配置时会const { viteConfigPath, configLoader } await getBuilderOptionsBuilderOptions(options); const projectRoot resolve(options.configDir, ..); const { config: { build: buildProperty undefined, ...userConfig } {} } (await loadConfigFromFile(configEnv, viteConfigPath, projectRoot, undefined, undefined, configLoader)) ?? {};其中projectRoot被解析为configDir的上一级目录即.storybook/的父目录随后viteConfigPath会连同projectRoot一起交给 Vite 的loadConfigFromFile解析。因此示例中形如../../../vite.config.js的相对路径其真实语义是从 Storybook 启动时的项目根目录向上回溯若干层级——这正是 Monorepo 中.storybook位于子包、而vite.config.js位于仓库根或 workspace 根时的经典写法。关键事实二会剥离用户 Vite 配置中的build字段上面代码中有一处刻意处理buildProperty被从用户 Vite 配置中解构剥离。源码注释解释如下I destructure away thebuildproperty from the users config object, because it can contain config that breaks Storybook… If the user needs to configure thebuildthey need to do so in the viteFinal function.也就是说如果你的vite.config.js中带有build相关配置builder-vite 不会直接采纳它们避免破坏 Storybook 自身的产物构建需要自定义build时必须改用 main 配置中的viteFinal钩子。关键事实三configLoader依赖 Vite 版本configLoader与 Vite 的--configLoader命令行标志等价取值可为bundle | runner | native。它要求Vite 6.1.0 及以上在旧版本 Vite 上会被静默忽略——升级 Vite 前需确认这一点避免配置失效却无报错。builder 与框架的加载关系从 core-common.ts 的BuilderName到框架预设的 preset 机制可以推断builder 的生效本质是把core.builder.name解析为对应包后调用其 presetviteFinal/webpackFinal、入口插件等。builder-vite 在 vite-config.ts 中先把用户 Vite 配置与自身的root: projectRoot、base: ./、Storybook 自包含插件合并再交给框架的viteFinal做最终调整——因此配置优先级大致为用户vite.config基础项 → builder 默认项 →viteFinal最终覆写。四、何时需要viteConfigPath单仓默认与 Monorepo 场景builder-vite 的 README.md 对自定义 Vite 配置给出了补充说明The builderwillread yourvite.config.jsfile, though it may change some of the options in order to work correctly. It looks for the Vite config in the CWD. If your config is located elsewhere, specify the path using theviteConfigPathbuilder option.据此可以明确两种典型用法默认单仓布局vite.config.js位于项目根目录、且 Storybook 从项目根目录启动时不需要配置viteConfigPathbuilder-vite 会自动在 CWD 下找到它非默认 / Monorepo 布局当vite.config.js不在 CWD例如位于 workspace 根、pnpm/yarn workspace 上级目录或命名并非标准的vite.config.*时必须通过core.builder.options.viteConfigPath或下文推荐的方式显式给出相对于项目根目录的路径。README 同时展示了在 React 框架下通过framework.options.builder而非core.builder传递同一选项的等价写法// .storybook/main.mjs export default { framework: { name: storybook/react-vite, // Your framework name here. options: { builder: { viteConfigPath: .storybook/customViteConfig.js, }, }, }, };注意这里viteConfigPath指向的是.storybook/内一个自定义 Vite 配置文件——路径同样是相对于process.cwd()解析的。五、优先使用framework.options.builder而非core.builder官方文档在core.builder一节给出了明确的使用倾向提示见 main-config-core.mdx在全新的 Framework API 下framework.options.builder已成为配置构建器的首选方式只有当需要配置不属于任何框架的构建器时才应使用core.builder.options。也就是说配置优先级建议如下框架自带构建器的常规项目无需显式配置框架默认绑定如react-vite→ builder-vitereact-webpack5→ builder-webpack5需要通过框架携带 builder 选项推荐使用framework.options.builder把 builder 配置与框架绑定随框架预设被一致解析参见 main-config-framework.mdx裸构建器 / 自定义集成此时 builder 不属于某个框架才在core.builder中声明name与options。从仓库源码结构看这一演进与 new-frameworks.mdx 描述的框架预设封装 builder设计一致framework 预设内部会声明并装配 builderframework.options.builder属于框架预设的第一方选项解析时机更早、类型更完整而core.builder是面向 core 层的通用后门主要用于历史配置兼容与脱离框架的构建器接入。六、Webpack 构建器与相邻core选项若使用 Webpack 5storybook/builder-webpack5包位于 code/builders/builder-webpack5可将core.builder.name替换为对应包名。与构建器直接相关的另一组core选项包括见 main-config-core.mdxdisableWebpackDefaultsboolean关闭 Storybook 对 Webpack 的默认配置注入适合需要在webpackFinal中完全接管配置的高级场景disableProjectJsonboolean关闭project.json元数据文件生成disableTelemetry/enableCrashReports控制遥测与崩溃上报allowedHostsstring[] | truedev server 的 Origin/Host 校验白名单配合反向代理访问本地 Storybook 时使用crossOriginIsolatedboolean注入Cross-Origin-Opener-Policy: same-origin与Cross-Origin-Embedder-Policy: require-corp头为 SharedArrayBuffer 等能力创建安全上下文。需要留意的是仓库中 core-common.ts 还定义了一组与 builder 包内部运行时有关的通用BuilderOptions含configType: DEVELOPMENT | PRODUCTION、configDir、disableWebpackDefaults、cache等——它们由 Storybook 在启动时注入 builder与你在core.builder.options中面向用户的选项如viteConfigPath是两层概念不要混淆。七、常见问题速查问题结论配置了core.builder.options.viteConfigPath却没生效确认路径是相对process.cwd()项目根目录而非.storybook/结合上一级目录数推演示例中../../../的含义自定义了vite.config.js里的build却不被采纳有意为之builder-vite 会剥离该字段请在viteFinal中配置构建行为configLoader不起作用需 Vite ≥ 6.1.0旧版本会被静默忽略框架已带 builder还需要core.builder吗不需要需要传 builder 选项时优先用framework.options.builder不使用任何框架的裸构建器使用core.builder的{ name, options }对象形式显式声明上述路径均可对照仓库源码进一步验证选项类型见 builder-vite/src/types.ts加载与剥离逻辑见 builder-vite/src/vite-config.tscore配置的公开类型见 main-config-core.mdx底层CoreConfig/BuilderName见 core-common.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),仅供参考
