
Storybook 定制指南在 webpackFinal 中通过 presets 获取 Webpack 版本与实例【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybookStorybook 的 Webpack 构建器允许你通过.storybook/main.js中的webpackFinal字段深度定制打包配置。本文以docs/_snippets/storybook-main-versioned-webpack.md代码片段为核心讲解如何在webpackFinal中利用presets.apply(webpackVersion)与presets.apply(webpackInstance)读取当前构建器使用的 Webpack 版本和实例对象并结合仓库源码剖析其底层实现原理帮助你在编写跨版本兼容的定制逻辑时做到有的放矢。一、背景Storybook 的 webpackFinal 配置入口Storybook 官方 Webpack 构建器builder文档位于 docs/builders/webpack.mdx其中明确说明Storybook 基于 Webpack 5 提供开箱即用的零配置支持同时允许你在 Storybook 配置文件.storybook/main.js|ts中提供webpackFinal配置项来扩展默认配置例如添加自定义 loader 或 plugin。webpackFinal的约定是导出一个函数第一个参数接收基线 Webpack 配置对象第二个参数接收 Storybook 的 options 对象包含配置目录、开发/生产模式等信息函数最终返回修改后的配置。完整的基础示例可参考 docs/_snippets/main-config-webpack-final.md其中展示了通过解构configType来区分DEVELOPMENT与PRODUCTION两种模式分别定制export default { framework: storybook/your-framework, stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], webpackFinal: async (config, { configType }) { if (configType DEVELOPMENT) { // Modify config for development } if (configType PRODUCTION) { // Modify config for production } return config; }, };值得注意的是webpackFinal的第二个参数options 对象不止包含configType它还携带了 Storybook 内部的presets对象。本文要讲解的版本感知定制方案正是借助这个presets对象完成的。二、核心代码解析版本感知的 webpackFinal关联文档 docs/_snippets/storybook-main-versioned-webpack.md 给出了一个版本感知versioned的webpackFinal写法完整内容如下export function webpackFinal(config, { presets }) { const version await presets.apply(webpackVersion); const instance (await presets.apply(webpackInstance))?.default; logger.info( Running in webpack ${version}: ${instance}); return config; }逐行拆解这段代码webpackFinal(config, { presets })解构出第二个参数中的presets。presets是 Storybook 的预设preset注册表可以通过presets.apply(key)获取注册在构建器预设中的某个导出值。apply返回的是一个 Promise因此需要await取结果。await presets.apply(webpackVersion)读取当前构建器声明的 Webpack 主版本号。在 Webpack 5 构建器中该值固定为字符串5。await presets.apply(webpackInstance)读取当前构建器使用的 Webpack 模块对象即import webpack from webpack得到的模块命名空间通过?.default取其默认导出——也就是真正的 webpack 函数/编译入口。logger.info(...)将解析结果输出到 Storybook 的 Node 进程日志便于在终端确认构建器实际使用的 Webpack 版本与实例来源。需要提醒的是片段中使用了await但函数本身未声明为async实际落地使用时请将函数声明为async function webpackFinal(...)或使用返回 Promise 的箭头函数形式否则会因顶层await语法问题报错。这一点在官方完整示例 docs/_snippets/main-config-webpack-final.md 中均以async形式呈现可以互为印证。三、源码级原理webpackVersion 与 webpackInstance 从何而来要理解这段代码需要深入 builder-webpack5 的预设实现。这两个 preset 值的定义位于 code/builders/builder-webpack5/src/presets/custom-webpack-preset.tsexport const webpackInstance async () webpackModule; export const webpackVersion async () 5;也就是说webpackVersion预设固定返回字符串5标识 builder-webpack5 只支持 Webpack 5Webpack 4 支持已被移除见 docs/builders/webpack.mdx 的故障排查章节webpackInstance预设返回webpackModule即该 preset 文件顶部import webpackModule from webpack引入的整个 webpack 模块。这两个 preset 通过该文件末尾的overridePresets导出被注册进 Storybook 的预设系统同一文件的 code/builders/builder-webpack5/src/presets/custom-webpack-preset.ts 中的overridePresets数组因此用户配置中的presets.apply(...)才能命中它们。更重要的是这两个 preset 不只是给用户日志用的——builder 自身在启动阶段就依赖它们解析真实的 webpack 实例。在 code/builders/builder-webpack5/src/index.ts 的executor.get()中export const executor { get: async (options: Options) { const version ((await options.presets.apply(webpackVersion)) || 5) as string; const webpackInstance (await options.presets.apply{ default: typeof webpackModule }(webpackInstance)) ?.default || webpackModule; checkWebpackVersion({ version }, 5, builder-webpack5); return webpackInstance; }, };这段实现揭示了三个关键事实builder 通过presets.apply(webpackVersion)拿到版本号并用|| 5兜底builder 通过presets.apply(webpackInstance)拿到模块后再取?.default兜底回退到自身导入的webpackModule——这与用户侧片段中的(await presets.apply(webpackInstance))?.default写法完全一致说明用户片段正是模仿了构建器内部的实际调用方式builder 调用checkWebpackVersion校验版本一致性该函数来自storybook/core-webpack位于 code/builders/builder-webpack5/src/index.ts 的导入版本不匹配时会抛出明确错误防止用错误的 Webpack 实例启动编译。随后builder 在开发模式的startercode/builders/builder-webpack5/src/index.ts和生产构建的builder同文件 code/builders/builder-webpack5/src/index.ts中分别调用executor.get(options)获得实例再执行webpackInstance(config)创建 compiler。由此可以推断用户侧在webpackFinal中通过同一对 preset 读取到的版本与实例正是 Storybook 实际用来执行编译的那一份可用于诊断到底是谁在打包。四、实战落地完整可运行的版本感知配置将上文片段修正为 async 形式并配合configType判断即可得到一份可直接放入.storybook/main.js的完整配置export default { framework: storybook/your-framework, // 例如 react-webpack5、nextjs、angular 等 stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], async webpackFinal(config, { presets, configType }) { // 读取当前构建器的 Webpack 版本与实例 const version await presets.apply(webpackVersion); const instance (await presets.apply(webpackInstance))?.default; if (configType DEVELOPMENT) { console.log([dev] Running in webpack ${version}: ${instance instance.version || unknown}); } if (configType PRODUCTION) { console.log([prod] Running in webpack ${version}: ${instance instance.version || unknown}); } // 示例追加一条自定义规则graphql-tag/loader 场景见 docs/_snippets/webpack-final-to-vite-final.md config.module?.rules?.push({ test: /\.(graphql|gql)$/, exclude: /node_modules/, loader: graphql-tag/loader, }); return config; }, };使用说明输出日志使用logger来自storybook/internal/node-logger见 code/builders/builder-webpack5/src/presets/custom-webpack-preset.ts 的导入用法可保持与 Storybook 一致的日志风格在浏览器控制台验证时也可直接使用console.log。在 TypeScript 项目中presets.apply的返回类型可通过泛型标注例如await presets.applystring(webpackVersion)与 builder 内部options.presets.apply{ default: typeof webpackModule }(webpackInstance)的用法对应参考 code/builders/builder-webpack5/src/index.ts。若只是想查看最终生效的 Webpack 配置官方还提供了调试命令开发模式运行yarn storybook dev --debug-webpack生产构建运行yarn storybook build --debug-webpack详见 docs/builders/webpack.mdx 的 Debug Webpack configuration 章节可以输出完整配置用于比对。五、定制 webpackFinal 的注意事项结合 docs/builders/webpack.mdx 与片段中保留config原样返回的写法有几个实操要点保留entry与outputStorybook 启动时会自动合并你返回的配置但使用webpackFinal时合并责任在你自身建议谨慎修改配置对象尤其不要破坏entry与output两个属性。向config.plugins追加而非整体覆盖Storybook 依赖HtmlWebpackPlugin生成预览页面若整体重写config.plugins可能丢失该插件导致预览页无法生成应使用config.plugins [...(config.plugins || []), YourPlugin]形式追加。loader 需排除.ejs如果自定义 loader 没有通过test属性明确限定文件扩展名需要为其显式exclude掉.ejs扩展名避免误伤 Storybook 的模板文件。区分 preview 与 managerwebpackFinal只作用于渲染 stories 的 preview iframeStorybook UImanager使用独立的打包流程managerWebpack相关配置已不再影响 UI 构建。编译器compiler无关性Storybook 支持 Babel 与 SWC 两种编译方案分别通过storybook/addon-webpack5-compiler-babel与storybook/addon-webpack5-compiler-swc接入你的webpackFinal定制应避免硬编码依赖某一种 loader以保持与项目既有编译链路的兼容。六、小结presets.apply(webpackVersion)与presets.apply(webpackInstance)是 Storybook 暴露给用户配置的自省接口它们不仅服务于用户侧日志输出更是 builder 内部解析编译实例、校验版本一致性的同一条调用链code/builders/builder-webpack5/src/index.ts。掌握这对 preset你就能够在webpackFinal中写出对构建器环境敏感、可在不同版本间稳定运行的定制逻辑——这正是 docs/_snippets/storybook-main-versioned-webpack.md 想要传达的核心模式。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考