news 2026/9/18 14:27:26

Storybook × Next.js:手动将 React 项目的 framework 切换到 @storybook/nextjs,并看懂其 Preset 实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Storybook × Next.js:手动将 React 项目的 framework 切换到 @storybook/nextjs,并看懂其 Preset 实现

Storybook × Next.js:手动将 React 项目的 framework 切换到 @storybook/nextjs,并看懂其 Preset 实现

本文以 Storybook 官方文档中「手动安装 Next.js framework」的核心配置片段为主体,完整讲解如何将一个已有的 React + Webpack 项目切换到@storybook/nextjs框架:从安装依赖、修改.storybook/main.js|ts中的framework字段,到清理不再需要的旧 Addon,并结合开源仓库源码深入解析这个字段背后触发的 Preset 解析、Builder/Renderer 装配与defineMain类型函数的真实行为。读完本文,你可以独立完成框架切换,并理解 Storybook 配置项与底层 preset 加载机制之间的对应关系。

1. 适用场景:为什么需要手动切换 framework

当你的项目最初使用@storybook/react-webpack5(或其他 React 框架 preset)初始化了 Storybook,但业务本身是 Next.js 应用时,官方推荐的做法是把整个框架 preset 切换为@storybook/nextjs。这一流程在官方文档 Next.js framework 页面的 FAQ「How do I manually install the Next.js framework?」 中给出了完整步骤,其核心配置片段正是 nextjs-add-framework.md 所定义的 diff 内容:修改framework属性并同步更换配置类型的 import 来源。

整个流程分为三步:

  1. 安装@storybook/nextjs开发依赖;
  2. 修改.storybook/main.js|ts,将framework从原框架改为@storybook/nextjs,并更换StorybookConfig类型或defineMain的 import 路径;
  3. 移除此前用于集成 Next.js 的第三方 Addon(如storybook-addon-next)。

以下各节依次展开这三步,并在第 5 节深入源码说明framework字段在 Storybook 内部究竟做了什么。

2. 第一步:安装 @storybook/nextjs 包

在修改任何配置之前,先按你的包管理器安装框架包(来自 nextjs-install.md):

# npm npm install --save-dev @storybook/nextjs
# pnpm pnpm add --save-dev @storybook/nextjs
# yarn yarn add --dev @storybook/nextjs

安装完成后,从 package.json 的peerDependencies可以确认适用前提:该 preset 要求项目满足next ^14.1.0 || ^15.0.0 || ^16.0.0react ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0,以及webpack ^5.0.0(在peerDependenciesMeta中标记为可选,因为 webpack 5 由 preset 内部依赖提供)。如果你的 Next.js 版本不在上述范围内,需要先升级 Next.js 再执行本切换。

3. 第二步:修改 .storybook/main.js|ts 中的 framework 属性

这是核心文档 nextjs-add-framework.md 的主体内容。该片段覆盖了两种配置写法风格(CSF 3 传统写法与 CSF Next 实验性写法)以及两种文件语言(.js/.ts),共四种 diff 变体,切换时请按你项目实际使用的风格对照执行。

3.1 CSF 3 风格:.storybook/main.js

最小改动只有一个字段:

export default { // ... - framework: '@storybook/react-webpack5', + framework: '@storybook/nextjs', };

3.2 CSF 3 风格:.storybook/main.ts

TypeScript 版本除了改framework字段外,还必须同步更换配置对象的类型来源——StorybookConfig类型由框架包提供,切换框架后 import 路径要从旧框架包改为@storybook/nextjs

- import type { StorybookConfig } from '@storybook/your-previous-framework'; + import type { StorybookConfig } from '@storybook/nextjs'; const config: StorybookConfig = { // ... - framework: '@storybook/react-webpack5', + framework: '@storybook/nextjs', }; export default config;

这里your-previous-framework是占位符,代表你当前正在使用的框架包名(如@storybook/react-webpack5)。之所以必须改 import,是因为StorybookConfig类型中framework字段的合法值与各框架扩展的 options 类型都由框架包自身声明,沿用旧包的类型会导致字段校验与新框架的 options(见第 5.5 节)不匹配。

3.3 CSF Next(实验性)风格:defineMain写法

如果你的main.ts|js采用实验性的defineMain辅助函数写法,改动点从类型 import 变为defineMain的 import 来源:

- import { defineMain } from '@storybook/your-previous-framework/node'; + import { defineMain } from '@storybook/nextjs/node'; export default defineMain({ // ... - framework: '@storybook/react-webpack5', + framework: '@storybook/nextjs', });

同一段配置在.storybook/main.js(JS 版 CSF Next 写法)中完全相同:

- import { defineMain } from '@storybook/your-previous-framework/node'; + import { defineMain } from '@storybook/nextjs/node'; export default defineMain({ // ... - framework: '@storybook/react-webpack5', + framework: '@storybook/nextjs', });

关于@storybook/nextjs/node子路径导出,可以在 src/node/index.ts 中确认其真实实现:

import type { StorybookConfig } from '../types.ts'; export function defineMain(config: StorybookConfig) { return config; } export type { StorybookConfig };

可以看到defineMain是一个恒等函数(identity function),运行时不做任何处理,它的全部价值在于静态层面:借助@storybook/nextjs/node子路径导出的、由该框架 types.ts 定义的StorybookConfig类型,让配置对象获得 Next.js 框架专属的字段补全与校验。因此把defineMain的 import 从旧框架包切到@storybook/nextjs/node,等价于把类型系统整体切换到 Next.js 框架的 schema 上。

4. 第三步:移除不再需要的 Next.js 集成 Addon

切换到@storybook/nextjs框架后,此前用于把 Next.js 特性「注入」到普通 React Storybook 的第三方 Addon 会被 preset 的原生能力取代,应当从addons数组中删除。根据 nextjs-remove-addons.md,可以移除的是:

export default { // ... addons: [ // ... // 👇 These can both be removed // 'storybook-addon-next', // 'storybook-addon-next-router', ], };

storybook-addon-nextstorybook-addon-next-router两个 Addon。TS 配置(CSF 3 或 CSF Next 写法)中的清理方式相同,只是配置外层包的是StorybookConfig对象或defineMain({...})调用。清理后请确认package.json中对应的依赖也一并卸载,避免残留依赖拉入与 preset 冲突的 webpack 插件。

5. 源码纵深:framework: '@storybook/nextjs'在 Storybook 内部触发了什么

配置层面改一个字段,但运行时 Storybook 会据此把整个构建链换掉。以下基于本仓库code/frameworks/nextjs的源码说明这条链路。

5.1 framework 字段即 preset 名称

Storybook 的framework字段在内部按 preset 规则解析:字符串值@storybook/nextjs会加载该包的 preset 入口。对应到本仓库就是 preset.js(一行 re-export 到构建产物dist/preset.js),其源码为 src/preset.ts。preset 文件按约定导出若干PresetProperty钩子,Storybook 在启动时逐个应用。

5.2 core 钩子:锁定 builder-webpack5 与 React renderer

src/preset.ts 的core钩子是切换后行为变化的核心:

  • 首先它通过options.presets.apply('framework')回读framework配置,并在 webpack 真正启动之前调用configureConfig加载 Next.js 的next.config.js配置。源码注释说明了原因:让 Next.js 有机会先行覆写 webpack 内部行为,否则@storybook/builder-webpack5的文件系统缓存(fsCache: true)无法正常工作。同时支持从对象形式的framework.options.nextConfigPath中读取自定义的 Next.js 配置路径;
  • 然后返回固定的构建链:builder指向@storybook/builder-webpack5(可选合并framework.options.builder),renderer指向@storybook/react/presetaddons钩子则自动追加@storybook/preset-react-webpack

这也解释了为什么 package.json 的 dependencies 中会直接依赖@storybook/builder-webpack5@storybook/react@storybook/preset-react-webpack——框架 preset 自身保证了构建链的完整性。

5.3 previewAnnotations:注入 preview 与 Next.js 运行时兼容层

previewAnnotations 钩子会在 preview 编译前自动注入@storybook/nextjs/preview注解(对应 src/preview.tsx);对于 Next.js 16 以下版本,还会额外注入@storybook/nextjs/config/preview(源码中留有 TODO,待只支持 Next.js 16+ 后移除)。这两个文件承载了路由 Provider、next/image装饰器、styled-jsx、head 管理等运行时能力,全部对用户透明——这正是「切换 framework 后无需再挂第三方 Addon」的原因。

5.4 babel 钩子:复用项目的 Next.js Babel 配置

pretset.ts 的babel钩子 会解析项目现有的 Babel 配置,识别其中的next/babelpreset(字符串、数组或含file.request的配置项三种形态),据此决定如何组装 Storybook 侧的 Babel 处理链(内置 src/babel/preset.ts 与若干 Next.js 兼容插件,如react-loadable-pluginoptimize-hook-destructuring等)。从源码结构看,这套机制保证你在 Next.js 项目中启用的 Babel 插件在 Storybook 里也能生效。

5.5 framework 的对象形式:nextConfigPathbuilder

从 core 钩子的实现 可以确认,framework除了字符串形式外还支持对象形式,其中options.nextConfigPath用于指定非默认的next.config.js位置,options.builder用于向@storybook/builder-webpack5透传 builder 级选项:

// 对应 preset.ts 中的读取逻辑 nextConfigPath: typeof framework === 'string' ? undefined : framework.options.nextConfigPath, // builder.options 合并自 ...(typeof framework === 'string' ? {} : framework.options.builder || {}),

这两个选项的完整类型定义位于 src/types.ts,即@storybook/nextjs包导出的StorybookConfig所依赖的类型源。

6. @storybook/nextjs 提供的能力边界

切换 framework 后获得哪些能力、边界在哪,可以直接从 package.json 的exports映射与 src 目录结构 相互印证。exports暴露的用户可用子路径包括:

子路径对应源码用途
@storybook/nextjs/previewsrc/preview.tsxpreview 运行时注入入口
@storybook/nextjs/nodesrc/node/index.tsdefineMainStorybookConfig类型
@storybook/nextjs/export-mocks(及headers.mocknavigation.mockrouter.mocklink.mock等)src/export-mocks/在 Storybook 中 mock Next.js 的headers/cookies/useRouter/usePathname等 API
@storybook/nextjs/images/next-imageimages/next-legacy-imagesrc/images/以 Next.js 方式渲染next/image
@storybook/nextjs/rsc/server-onlysrc/rsc/server-only.ts实验性 React Server Components 支持所需的桩模块
@storybook/nextjs/storybook-nextjs-font-loadersrc/font/webpack/loader/next/font的字体加载 Webpack loader

src目录结构看,preset 还内置了:routing/(App Router 与 Pages Router 两种 Provider 的装饰器)、styledJsx/(styled-jsx 编译支持)、swc/(Next.js SWC loader 补丁)、nodePolyfills/(Node 模块 polyfill,由依赖node-polyfill-webpack-plugin驱动)、aliases/imports/(基于tsconfig-paths-webpack-plugin复用项目的tsconfig.json路径别名)等。依赖列表中的styled-jsxprobe-image-sizereact-refresh/@pmmmwh/react-refresh-webpack-plugin等也都与这些能力一一对应。

结合官方 FAQ(nextjs.mdx)还有两个切换后必须知道的行为变化与限制:

  • 图片导入语义变化:启用该框架后,静态图片 import 返回的是{ src, height, width, blurDataURL }对象(Next.js 方式),而不再是裸路径字符串,故事中处理图片的地方需要相应调整;
  • Yarn v2/v3 用户注意:由于 Yarn 2/3 的包解析规则不同,可能出现Can't resolve 'css-loader'/'style-loader'报错,此时需要把这两个 loader 直接安装为项目依赖;
  • 数据获取型页面app目录中直接 fetch 数据的页面组件引入 Node 专用模块会导致 Webpack 构建崩溃,官方建议把纯组件拆到单独文件供故事使用,或在webpackFinal中 polyfill 相关模块。

7. 小结与验证路径

  • 手动切换@storybook/nextjs框架的完整动作只有三处:安装依赖、把framework改为@storybook/nextjs并同步更换StorybookConfig/defineMain的 import 来源、移除storybook-addon-next(-router)类旧 Addon;
  • framework字段在运行时等价于「加载该包的 preset」,@storybook/nextjs的 preset(src/preset.ts)负责把 builder 锁定为@storybook/builder-webpack5、renderer 锁定为 React preset,并在启动前加载next.config.js
  • defineMain是纯类型层面的恒等函数,其意义在于把配置对象绑定到@storybook/nextjs/node导出的框架类型上;
  • 切换后 Next.js 的图片、字体、路由、headers/routermock 等能力由 preset 原生提供,能力清单与边界可从 package.json 的exports/peerDependencies精确核对(当前仓库版本为10.6.0-beta.1,适用 Next.js 14.1+/15/16)。

关键参考文件:核心配置片段、安装命令片段、旧 Addon 清理片段、Next.js framework 官方文档页、preset 入口、preset 源码、node 子路径导出、包清单。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 14:26:41

FMEA知识与操作实务:从RPN评分到AP优先级与Python自动化

简介:PPT培训课件系统讲解FMEA(失效模式与效应分析)知识与操作实务,适合企业质量工程师、研发设计人员、生产制造及工艺管理人员学习参考。内容从FMEA发展历程和应用分类入手,涵盖DFMEA与PFMEA两大类型,梳理…

作者头像 李华
网站建设 2026/9/18 14:25:46

智慧补货系统:构建数据闭环驱动的动态库存决策

简介:本资源是IBM推出的《智慧补货解决方案》专业资料,面向零售企业运营管理者、供应链决策者及数字化转型实践者,聚焦解决传统依赖经验的粗放式补货导致的缺货、积压与区域适配失准等核心痛点。文档系统阐述了以数据驱动的多维智能补货模型&…

作者头像 李华
网站建设 2026/9/18 14:25:17

以创新赋能科研提速 为高质量发展注入硬核动能

作为研究生,科研任务多如牛毛:海量文献阅读、实验进度跟踪、论文写作、团队协作…… 稍不留神就容易乱成一锅粥。 今天,我为你精选四款 2026 年超实用的科研任务管理工具,帮你从文献搜集到项目推进全流程提效。它们覆盖不同环节&…

作者头像 李华
网站建设 2026/9/18 14:24:11

数据结构第六章树和二叉树:高频课后题解析与避坑指南

第六章树和二叉树,是很多人学数据结构时第一次被劝退的地方。前五章的线性表、栈、队列就算没太懂,硬背几遍代码也能撑过考试;到这一章,递归、指针、遍历、线索化全搅在一起,选择题开始玩文字游戏,算法题也…

作者头像 李华
网站建设 2026/9/18 14:23:46

OllyDbg完全入门:下载安装到断点调试实战指南

1. 写在最前面:为什么要从OllyDbg开始OllyDbg,圈内人通常直接叫它“OD”,是Windows平台上一款经典的32位用户态调试器。很多刚接触软件分析、逆向工程或者二进制安全的朋友,第一次听说“调试器”这个概念,十有八九都是…

作者头像 李华