
React 伺服器元件的 Storybook
透過升級至 Storybook 8.0 alpha 版本,在 Storybook 中使用 RSC

React 伺服器元件 (RSC) 是基於 React 的 Web UI 的全新程式設計模型。與傳統的 React「客戶端」元件相比,它們僅在伺服器上渲染。這帶來了各種效能和安全性優勢,但這也與我們今天使用的 React 工具和函式庫大相逕庭。
受影響最大的領域之一是元件驅動開發和測試。諸如 Storybook、Testing Library 和 Playwright/Cypress 元件測試之類的工具都假設使用者的元件正在瀏覽器(或 JSDom)中渲染。但是對於伺服器元件,情況不再如此。
這產生了一個問題:對於伺服器,隔離的元件開發和測試意味著什麼?
今天,我很高興發布 Storybook 的 Next.js 框架中的 RSC 支援,作為對這個問題的實驗性解答。它是一個純客戶端實作,使其與 Storybook 擴充套件和整合的整個生態系統相容。
繼續閱讀以了解它是如何運作、如何使用它,以及您今天如何試用它!
伺服器來自火星,客戶端來自金星
RSC 與傳統客戶端元件有兩個主要區別,這兩個區別都存在於以下範例中
// ApiCard.tsx
import { ComponentProps } from 'react';
import { Card } from './Card';
import { findById } from './db';
export async function DbCard({ id }: {id: number}) {
let props;
try {
const contact = await findById(id);
props = { state: 'success', contact };
} catch (e) {
props = { state: 'error' };
}
return <Card {...props} />;
}
- 第一個區別是我們的元件是
async
,客戶端不支援此功能。 - 第二個區別是我們的元件可以直接存取 Node 程式碼,在本例中是包裝已驗證資料庫連線的
findById
函數。
RSC 在底層做了很多工作來實作這兩個區別。此程式碼僅在伺服器上執行,並且產生靜態 JSON 類型的結構,該結構被串流傳輸到客戶端。
Storybook 是一個純客戶端應用程式。它產生純 HTML/CSS/JS 的靜態建置,沒有任何 Node!因此,支援 RSC 需要弄清楚如何在客戶端上渲染 RSC,或者為伺服器重新架構 Storybook。
我們首先專注於客戶端方法。我們希望最大限度地減少對使用者的影響,他們已經編寫了數百萬個故事和數百個擴充套件,所有這些都基於目前的架構。
那麼,它到底是如何運作的呢?
開始使用 async
在客戶端渲染 RSC 的第一個挑戰是配置如何支援 async 元件。事實證明,Next.js 的 canary React 版本已經 (非官方地) 支援此功能。特別感謝 JamesManningR 和 julRuss,他們貢獻了這個簡單的解決方案!
import { Suspense } from 'react';
export const ClientContact = ({ id }) => (
<Suspense><DbCard id={id} /></Suspense>
);
從 Storybook 8 開始,@storybook/nextjs
可以使用 .storybook/main.js
中的 experimentalRSC
功能標誌將您的故事包裝在 Suspense
中
// .storybook/main.js
export default {
features: {
experimentalRSC: true,
}
};
您也可以在 7.x 版本的 @storybook/nextjs
中手動執行此操作,方法是將您的 RSC 故事包裝在 裝飾器 中。
注意:此解決方案尚無法在其他 Storybook React 框架(例如 react-vite
、react-webpack5
)中運作,因為它們未使用 Next.js 的 canary 版本 React。希望下一個版本的 React 會消除此限制。
模擬和載入
解決 async 問題僅使我們完成了一半。我們的 DbCard
元件也參考了節點程式碼,該程式碼擷取資料以填充元件。這在瀏覽器中是一個問題,瀏覽器無法執行 Node 程式碼!
為了解決這個問題,我們建議建立一個乾淨的資料存取層。RSC 的架構師也 建議將其作為最佳實務。
建立資料存取層後,您可以模擬它,使其可以在瀏覽器中執行,並且您可以精確控制它返回的資料,以練習不同的 UI 狀態(載入中、錯誤、成功等)。
您可以使用模組模擬或網路模擬來模擬資料存取層,Storybook 都支援這兩種模擬。
模組:有一個社群擴充套件 storybook-addon-module-mock,它提供 jest.mock
樣式的模擬(僅適用於 Webpack 專案)。您也可以使用 webpack/vite 別名來獲得更簡單但更有限的解決方案。我們計劃在未來版本的 Storybook 中提供符合人體工學的模組模擬。
網路 API:為了模擬網路請求,我們建議使用 Mock Service Worker (msw)。Storybook 也支援許多其他 網路 和 GraphQL 模擬擴充套件。
回到我們的範例,以下是使用 storybook-addon-module-mock
的故事可能看起來的樣子
// DbCard.stories.js
import { StoryObj, Meta } from '@storybook/react';
import { createMock } from 'storybook-addon-module-mock';
import { DbCard } from './DbCard';
import * as db from './db';
export default { component: DbCard };
export const Success {
args: { id: 1 },
parameters: {
moduleMock: {
mock: () => {
const mock = createMock(db, 'findById');
mock.mockReturnValue(Promise.resolve({
name: 'Beyonce',
img: 'https://blackhistorywall.files.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg',
tel: '+123 456 789',
email: 'b@beyonce.com'
}))
return [mock];
},
},
},
}
完整示範:API + 模組模擬
對於上面的完整範例,包括模組模擬資料庫版本和 MSW2 模擬 API 版本,請查看 我們的完整 RSC 示範 Storybook 或 其 GitHub 儲存庫。

有什麼問題?
在這篇文章中,我們成功地為 Storybook 中的第一個 RSC 編寫了一個故事,並展示了這一切是如何在底層實作的。
這一切都很簡單明瞭,但這種方法有局限性
- 逼真度。純客戶端實作與在您的應用程式中執行的伺服器端串流 RSC 實作截然不同。
- 便利性。此處的模擬解決方案肯定可以改進。我們目前的模組模擬解決方案不僅冗長,而且與 Storybook args/controls 不相容。
我們計劃在後續迭代中解決這兩個限制,這就是我們將此解決方案標記為實驗性的原因。
立即使用 Storybook for RSC 🎊
若要使用 Storybook for RSC,請將您的 Storybook 升級到 8.0-alpha
npx storybook@next upgrade --prerelease
然後,在您的 .storybook/main.ts
中啟用實驗性功能
// .storybook/main.js
export default {
features: {
experimentalRSC: true,
},
};
如需更多資訊,請參閱 @storybook/nextjs
文件。
這是我們詳細介紹 Storybook 8.0(我們的下一個主要版本)內容的第一篇文章,我們將在未來幾個月內推出更多內容。請在 社群媒體上追蹤我們或註冊 Storybook 電子報,以隨時掌握有關下一個版本的所有新聞!
Storybook 現在支援 React 伺服器元件!
— Storybook (@storybookjs) 2023 年 12 月 13 日
了解其運作方式,並立即在 Storybook 8.0 alpha 版本中試用 🔥https://127.0.0.1/lOuql2kKPq