Storybook Addon Next
⚠️ 已弃用 ⚠️
此插件已弃用,取而代之的是 @storybook/nextjs,它是 Storybook 为支持 Storybook 中的 Next.js 功能而推出的官方插件。它支持 storybook-addon-next
的所有功能,以及更多!我甚至参与了与他们的合作开发,所以你应该可以放心使用。
请查看 迁移文档,了解有关如何迁移的详细信息。
😱 对 Next.js 的无需配置支持:厌倦了编写和调试 webpack 配置?Next.js 开箱即用支持的功能,此插件可以在 Storybook 中实现。
目录
支持的功能
👉 Postcss
👉 绝对导入
👉 运行时配置
需求
- Next.js >= 9.x
- Storybook >= 6.x
- 您的 Next.js 配置文件使用
.js
扩展名,而不是.mjs
扩展名(即next.config.js
而不是next.config.mjs
)- 有关详细信息,请参阅 next.config.js
示例
要运行任何示例,首先通过在该仓库的根目录中运行
yarn build
来构建插件。
- Nextjs v13 - 来源
- Nextjs v12 - 来源
- Tailwindcss - 来源
- SVGR - 来源
- Nx - 来源
- Nextjs v11.1 - 来源
- Nextjs v11.0 - 来源
- Nextjs v10 - 来源
- Nextjs v9 - 来源
入门
安装
使用 yarn
安装 storybook-addon-next
yarn add --dev storybook-addon-next
或者 npm
npm install --save-dev storybook-addon-next
在 main.js 中注册插件
// .storybook/main.js
module.exports = {
// other config ommited for brevity
addons: [
// ...
'storybook-addon-next'
// ...
]
}
派对
🥳🎉 就这些!支持的功能 应该可以开箱即用。
有关支持的功能在此插件中的工作方式的更多详细信息,请参阅 文档。
如果某些功能没有按预期工作,请随时 打开一个问题。
文档
选项
如果需要,可以将一个选项对象传递给此插件以进行额外配置。
例如
// .storybook/main.js
const path = require('path')
module.exports = {
// other config ommited for brevity
addons: [
// ...
{
name: 'storybook-addon-next',
options: {
nextConfigPath: path.resolve(__dirname, '../next.config.js')
}
}
// ...
]
}
nextConfigPath
:next.config.js
的绝对路径
Next.js 的 Image 组件(部分支持)
next/image 是出了名的难 用 storybook 来实现。此插件允许您在无需配置的情况下使用 Next.js 的 Image
组件!
由于图片组件的功能,如图片优化,是由选项配置的,这些选项需要 Next.js 配置文件才能被框架读取和处理,而 Next.js 没有公开函数来解析和处理这些选项,因此无法稳定地支持这些功能。
如果您想看到对这方面的更好支持,请随时参与 Next.js 方面的讨论 或 我们这边的讨论
本地图片
本地图片 与此插件配合使用效果很好!请记住,此功能 仅在 Next.js v11 中添加。
import Image from 'next/image'
import profilePic from '../public/me.png'
function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src={profilePic}
alt="Picture of the author"
// width={500} automatically provided
// height={500} automatically provided
// blurDataURL="../public/me.png" set to equal the image itself (for this addon)
// placeholder="blur" // Optional blur-up while loading
/>
<p>Welcome to my homepage!</p>
</>
)
}
远程图片
远程图片 也运行良好!
import Image from 'next/image'
export default function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src="/me.png"
alt="Picture of the author"
width={500}
height={500}
/>
<p>Welcome to my homepage!</p>
</>
)
}
优化
所有 Next.js Image
都将自动为您 取消优化。
如果使用 placeholder="blur",则使用的 blurDataURL 将是图片的 src(从而实际上禁用占位符)。
有关 Next.js Image
在 Storybook 中如何处理的更多讨论,请参阅 此问题。
AVIF
此插件尚不支持此格式。如果您想看到此功能,请随时 打开一个问题。
Next.js 路由
此解决方案很大程度上基于 storybook-addon-next-router,因此要感谢 lifeiscontent
提供了一个良好的解决方案,此插件可以借鉴。
Next.js 的路由器 将自动为您存根,以便在与路由器交互时,所有交互将自动记录到 Storybook actions 选项卡(如果您有 actions 插件)。
覆盖默认值
可以通过在故事 参数 上添加一个 nextRouter
属性来执行每故事覆盖。插件将浅层合并您在此处放置的任何内容到路由器中。
import SomeComponentThatUsesTheRouter from "./SomeComponentThatUsesTheRouter";
export default {
title: "My Story",
};
// if you have the actions addon
// you can click the links and see the route change events there
export const Example = () => <SomeComponentThatUsesTheRouter />;
Example.parameters: {
nextRouter: {
path: "/profile/[id]",
asPath: "/profile/ryanclementshax",
query: {
id: "ryanclementshax"
}
}
}
请查看此 示例 以作参考。
全局默认值
可以在 preview.js 中设置全局默认值,并将浅层合并到默认路由器中。
export const parameters = {
nextRouter: {
path: '/some-default-path',
asPath: '/some-default-path',
query: {}
}
}
请查看此 示例 以作参考。
默认路由器
存根路由器的默认值如下(有关全局变量的工作原理的更多详细信息,请参阅 全局变量)
const defaultRouter = {
locale: context?.globals?.locale,
route: '/',
pathname: '/',
query: {},
asPath: '/',
push(...args: unknown[]) {
action('nextRouter.push')(...args)
return Promise.resolve(true)
},
replace(...args: unknown[]) {
action('nextRouter.replace')(...args)
return Promise.resolve(true)
},
reload(...args: unknown[]) {
action('nextRouter.reload')(...args)
},
back(...args: unknown[]) {
action('nextRouter.back')(...args)
},
prefetch(...args: unknown[]) {
action('nextRouter.prefetch')(...args)
return Promise.resolve()
},
beforePopState(...args: unknown[]) {
action('nextRouter.beforePopState')(...args)
},
events: {
on(...args: unknown[]) {
action('nextRouter.events.on')(...args)
},
off(...args: unknown[]) {
action('nextRouter.events.off')(...args)
},
emit(...args: unknown[]) {
action('nextRouter.events.emit')(...args)
}
},
isFallback: false
}
Actions 集成注意事项
如果您覆盖了某个函数,您将失去自动 actions 选项卡集成,并必须自行构建。
export const parameters = {
nextRouter: {
push() {
// we lose the default implementation that logs the action into the action tab
}
}
}
自己动手操作如下(确保安装了 @storybook/addon-actions
包)
import { action } from '@storybook/addon-actions'
export const parameters = {
nextRouter: {
push(...args) {
// custom logic can go here
// this logs to the actions tab
action('nextRouter.push')(...args)
// return whatever you want here
return Promise.resolve(true)
}
}
}
Sass/Scss
全局 sass/scss 样式表 也支持,无需任何额外配置。只需将其导入到 preview.js 中即可
import '../styles/globals.scss'
这将自动在您的 next.config.js
文件中包含任何 自定义 sass 配置。
目前仅支持 Next.js 配置文件的
.js
扩展名,不支持.mjs
。有关详细信息,请参阅 next.config.js
const path = require('path')
module.exports = {
// any options here are included in sass compilation for your stories
sassOptions: {
includePaths: [path.join(__dirname, 'styles')]
}
}
Css/Sass/Scss 模块
Next.js 开箱即用地支持 css 模块,因此此插件也支持它。
// this import works just fine in Storybook now
import styles from './Button.module.css'
// sass/scss is also supported
// import styles from './Button.module.scss'
// import styles from './Button.module.sass'
export function Button() {
return (
<button type="button" className={styles.error}>
Destroy
</button>
)
}
Styled JSX
Next.js 的内置 CSS in JS 解决方案是 styled-jsx,此插件也开箱即用地支持它,无需配置。
// This works just fine in Storybook with this addon
function HelloWorld() {
return (
<div>
Hello world
<p>scoped!</p>
<style jsx>{`
p {
color: blue;
}
div {
background: red;
}
@media (max-width: 600px) {
div {
background: blue;
}
}
`}</style>
<style global jsx>{`
body {
background: black;
}
`}</style>
</div>
)
}
export default HelloWorld
您也可以使用自己的 babel 配置。这是一个自定义 styled-jsx 的示例。
// .babelrc or whatever config file you use
{
"presets": [
[
"next/babel",
{
"styled-jsx": {
"plugins": ["@styled-jsx/plugin-sass"]
}
}
]
]
}
如果您使用的是单仓库,则可能需要将 babel 配置自己添加到 storybook 项目中。只需将一个 babel 配置添加到您的 storybook 项目中,其中包含以下内容即可开始。
{
"presets": ["next/babel"]
}
Postcss
Next.js 允许您 自定义 postcss 配置。因此,此插件将自动为您处理 postcss 配置。
这使得您可以实现一些很酷的功能,比如无需配置的 tailwindcss!请查看 with-tailwindcss 示例 以作参考!它复制了 Next.js 的 tailwindcss 示例,并使用 storybook 和此插件进行了设置。
绝对导入
告别 ../
!从根目录进行的绝对导入与此插件配合使用效果很好。
// All good!
import Button from 'components/button'
// Also good!
import styles from 'styles/HomePage.module.css'
export default function HomePage() {
return (
<>
<h1 className={styles.title}>Hello World</h1>
<Button />
</>
)
}
// preview.js
// Also ok in preview.js!
import 'styles/globals.scss'
// ...
运行时配置
Next.js 允许 运行时配置,它让您可以导入一个方便的 getConfig
函数,以在运行时获取 next.config.js
文件中定义的某些配置。
在使用此插件的 Storybook 上下文中,您可以预期 Next.js 的 运行时配置 功能可以正常运行。
注意,由于 Storybook 不会服务器端渲染您的组件,您的组件只能看到他们在客户端通常看到的内容(即,他们不会看到serverRuntimeConfig
,但会看到publicRuntimeConfig
)。
例如,考虑以下 Next.js 配置
// next.config.js
module.exports = {
serverRuntimeConfig: {
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET // Pass through env variables
},
publicRuntimeConfig: {
staticFolder: '/static'
}
}
当在 Storybook 中调用时,对getConfig
的调用将返回以下对象
{
"serverRuntimeConfig": {},
"publicRuntimeConfig": {
"staticFolder": "/static"
}
}
自定义 Webpack 配置
Next.js 在开箱即用时提供了许多免费的功能,例如 Sass 支持,但有时我们会添加对 Next.js 的自定义 webpack 配置修改。此附加组件可以处理您想要添加的大多数 webpack 修改。如果 Next.js 开箱即用地支持某个功能,那么此附加组件将使该功能在 Storybook 中开箱即用。如果 Next.js 没有开箱即用地支持某些功能,但易于配置,那么此附加组件也会在 Storybook 中对该功能执行相同的操作。如果您发现仍然需要配置 webpack 才能在添加此附加组件后使 Next.js 功能在 Storybook 中正常工作,这很可能是错误,请随时打开一个问题。
任何希望用于 Storybook 的 webpack 修改都应在.storybook/main.js 中根据 Storybook 的文档进行。
注意:并非所有 webpack 修改都可以在next.config.js
和.storybook/main.js
之间复制粘贴。建议您研究如何正确地对 Storybook 的 webpack 配置进行修改,以及webpack 的工作原理。
请随时贡献一个示例来帮助社区。
以下是如何使用此附加组件在 Storybook 中添加 svgr 支持的示例。完整的示例可以找到这里。
// .storybook/main.js
module.exports = {
// other config omitted for brevity
webpackFinal: async config => {
// this modifies the existing image rule to exclude .svg files
// since we want to handle those files with @svgr/webpack
const imageRule = config.module.rules.find(rule => rule.test.test('.svg'))
imageRule.exclude = /\.svg$/
// configure .svg files to be loaded with @svgr/webpack
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack']
})
return config
}
}
Typescript
Storybook 处理大多数Typescript 配置,但此附加组件为 Next.js 对绝对导入和模块路径别名的支持提供了额外的支持。简而言之,它会考虑您的tsconfig.json
的baseUrl 和paths。因此,像下面这样的tsconfig.json
会开箱即用。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"]
}
}
}
next.config.js
ESM
目前,此插件支持的 Next.js 配置格式仅限于配置的 commonjs 版本(即next.config.js
)。这主要是因为我还没有弄清楚如何从 storybook 附加组件中(据我所知,它目前绑定到 commonjs 模块)中要求一个.mjs
文件。如果您能够提供帮助,如果您能为此讨论做出贡献以获得对.mjs
版本的支持,我将不胜感激,即使这种支持是可能的。
针对 Yarn v2 和 v3 用户的注意事项
如果您使用的是Yarn v2 或 v3,您可能会遇到 Storybook 无法解析style-loader
或css-loader
的问题。例如,您可能会遇到以下错误:
模块未找到:错误:无法解析 'css-loader'
模块未找到:错误:无法解析 'style-loader'
这是因为这些版本的 Yarn 与 Yarn v1.x 具有不同的包解析规则。如果您的情况也是如此,只需直接安装该包。
常见问题解答
静态导入的图片无法加载
确保您以与在正常开发中使用 next image 时相同的方式处理图像导入。
在storybook-addon-next
之前,图像导入只是导入到图像的原始路径(例如,'static/media/stories/assets/plugin.svg'
)。当使用storybook-addon-next
时,图像导入以“Next.js 方式”工作,这意味着我们现在在导入图像时会得到一个对象。例如
{
"src": "static/media/stories/assets/plugin.svg",
"height": 48,
"width": 48,
"blurDataURL": "static/media/stories/assets/plugin.svg"
}
因此,如果 Storybook 中的某些内容没有正确显示图像,请确保您期望从导入中返回对象,而不仅仅是资源路径。
有关 Next.js 如何处理静态图像导入的更多详细信息,请参见本地图像。
当使用 next 配置文件的 .mjs 扩展名时,此插件会中断
目前,此附加组件不支持使用next.config.mjs
。有关更多详细信息,请参见next.config.js。目前,您需要使用.js
扩展名。欢迎为此讨论做出贡献,以获得对它的支持。
模块未找到:错误:无法解析 [包名]
如果您使用的是 Yarn v2 或 v3,则可能会遇到这种情况。有关更多详细信息,请参见Yarn v2 和 v3 用户的说明。
类似项目
想建议其他功能吗?
我乐意与您讨论。请随时打开一个问题。
没有找到您要查找的内容?
这份文档对您来说不够充分吗?
它是否令人困惑?
它是否……我敢说……不准确?
如果以上任何一项描述了您对这份文档的感受。请随时打开一个问题。