Storybook 外掛 Next
⚠️ 已棄用 ⚠️
此外掛已被棄用,改用 @storybook/nextjs,這是 Storybook 官方外掛,用於支援 Storybook 中的 Next.js 功能。它支援 storybook-addon-next
的所有功能,而且功能更多!我甚至與他們一同開發此外掛,因此您應該可以放心使用。
請參閱遷移文件,以了解如何遷移的詳細資訊。
😱 無需設定即可支援 Next.js:厭倦了撰寫和偵錯 webpack 設定檔嗎?這個外掛可讓 Storybook 支援 Next.js 開箱即用的功能
目錄
支援的功能
👉 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(因此實際上停用了預留位置)。
請參閱此問題,以取得有關如何處理 Storybook 的 Next.js Image
的更多討論。
AVIF
此外掛尚不支援此格式。如果您想要看到此功能,請隨時開啟問題。
Next.js 路由
此解決方案很大程度上基於 storybook-addon-next-router,因此非常感謝 lifeiscontent
提供此外掛可以依賴的良好解決方案。
系統會自動為您建立 Next.js 的路由器的存根,因此當與路由器互動時,如果您有 actions 外掛,其所有互動都會自動記錄到 Storybook 動作索引標籤。
覆寫預設值
可透過將 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 整合注意事項
如果您覆寫函式,則會失去自動動作索引標籤整合,並且必須自行建構出來。
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 的文件指示,在 .storybook/main.js 中進行。
請注意:並非所有 webpack 的修改都可以在 next.config.js
和 .storybook/main.js
之間直接複製貼上。建議您研究如何正確地修改 Storybook 的 webpack 配置,以及 webpack 的運作原理。
歡迎貢獻範例,以協助社群。
以下是如何透過此 addon 為 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 設定,但此 addon 額外增加了對 Next.js 的絕對路徑導入和模組路徑別名的支援。簡而言之,它會考量到您的 tsconfig.json
中的 baseUrl 和 paths。因此,如下所示的 tsconfig.json
將可以直接使用。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"]
}
}
}
next.config.js
ESM
目前此插件僅支援 Next.js 設定的 commonjs 版本 (即 next.config.js
)。這主要是因為我尚未找出如何從 Storybook addon (目前已知 Storybook addon 綁定於 commonjs 模組) 中引入 .mjs
檔案的方法。如果您能夠幫忙,我非常樂意您參與此討論,以取得對 .mjs
版本的支援 (如果這種支援是可能的話)。
Yarn v2 和 v3 使用者的注意事項
如果您使用的是 Yarn v2 或 v3,您可能會遇到 Storybook 無法解析 style-loader
或 css-loader
的問題。例如,您可能會收到類似以下的錯誤:
Module not found: Error: Can't resolve 'css-loader'
Module not found: Error: Can't resolve '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 副檔名時,此外掛會損壞
目前此 addon 不支援使用 next.config.mjs
。請參閱next.config.js 了解更多詳細資訊。目前,您必須改用 .js
副檔名。歡迎您參與此討論,以取得此功能支援。
模組找不到:錯誤:無法解析 [套件名稱]
如果您使用的是 Yarn v2 或 v3,您可能會遇到此問題。請參閱Yarn v2 和 v3 使用者注意事項 了解更多詳細資訊。
類似專案
想要建議其他功能嗎?
我樂於接受討論。歡迎提出 issue。
找不到您要的內容嗎?
這份文件對您來說是否不夠完善?
是否令人困惑?
是否...我敢說...不夠準確?
如果以上任何描述符合您對這份文件的感受。歡迎提出 issue。