返回博客

用于 React 服务端组件的 Storybook

通过升级到 Storybook 8.0 alpha 在 Storybook 中使用 RSC

loading
Michael Shilman
@mshilman
最后更新

React 服务端组件 (RSC) 是一个用于基于 React 的 Web UI 的新型编程模型。与传统的 React “客户端” 组件相比,它们在服务器端渲染。这带来了各种性能和安全优势,但它也与我们今天使用的 React 工具和库大相径庭。

受影响最大的领域之一是组件驱动的开发和测试。像 Storybook、Testing Library 和 Playwright/Cypress Component Testing 这样的工具都假设用户的组件是在浏览器(或 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} />;
}
  1. 第一个区别是我们的组件是 async,客户端不支持异步组件。
  2. 第二个区别是我们的组件可以直接访问 Node 代码,在本例中是包装经过身份验证的数据库连接的 findById 函数。

RSC 在底层做了很多工作来实现这两个区别。这段代码仅在服务器端运行,并生成一个静态的类似 JSON 的结构,该结构被流式传输到客户端。

Storybook 是一个纯客户端应用程序。它生成纯 HTML/CSS/JS 的静态构建,看不到 Node!因此,支持 RSC 将需要弄清楚如何让 RSC 在客户端渲染,或者为服务器重新架构 Storybook。

我们首先专注于客户端方法。我们希望最大限度地减少对用户的影响,他们已经编写了数百万个 stories 和数百个插件,所有这些都基于当前的架构。

那么,这到底是如何工作的呢?

开始使用异步

让 RSC 在客户端渲染的第一个挑战是配置如何支持异步组件。事实证明,这在 Next.js 的 canary React 版本中已经(非官方地)支持了。特别感谢 JamesManningRjulRuss,他们贡献了这个简单的解决方案!

import { Suspense } from 'react';

export const ClientContact = ({ id }) => (
  <Suspense><DbCard id={id} /></Suspense>
);

从 Storybook 8 开始,@storybook/nextjs 可以使用 .storybook/main.js 中的 experimentalRSC 功能标志将您的 stories 包装在 Suspense

// .storybook/main.js
export default {
  features: {
    experimentalRSC: true,
  }
};

您还可以在 7.x 版本的 @storybook/nextjs 中手动执行此操作,方法是将您的 RSC stories 包装在装饰器中。

注意:此解决方案尚不适用于其他 Storybook React 框架(例如 react-vitereact-webpack5),因为它们不使用 Next.js 的 canary 版本 React。希望此限制将在下一个版本的 React 中取消。

已模拟和加载

解决异步问题只完成了我们一半的路程。我们的 DbCard 组件还引用了 node 代码,该代码获取数据以填充组件。这在浏览器中是一个问题,因为浏览器无法执行 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 的 story 可能看起来的样子

// 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 仓库

A contact card for Chuck Norris (with the email address gmail@chucknorris.com), demonstrated within Storybook

有什么缺点?

在这篇文章中,我们成功地为 Storybook 中的第一个 RSC 编写了一个 story,并展示了这一切是如何在底层实现的。

这一切都很简单明了,但是这种方法存在局限性

  1. 保真度。 纯客户端实现与在您的应用程序中运行的服务器端、流式 RSC 实现截然不同。
  2. 便利性。 这里的模拟解决方案绝对可以改进。我们当前的模块模拟解决方案不仅冗长,而且与 Storybook args/controls 不能很好地配合使用。

我们计划在后续迭代中解决这两个局限性,这就是为什么我们将此解决方案标记为实验性的。

立即将 Storybook 用于 RSC 🎊

要将 Storybook 用于 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 邮件列表

获取最新新闻、更新和发布

6,730位开发者及更多

我们正在招聘!

加入 Storybook 和 Chromatic 背后的团队。构建被数十万开发者在生产环境中使用的工具。远程优先。

查看职位

热门文章

如何让 Storybook 速度提升 2-4 倍

优化 Storybook 7.6 的构建性能
loading
Kasper Peulen

Storybook 在 2024 年的未来

2023 年的亮点以及接下来的内容
loading
Michael Shilman

构建您自己的 Storybook GPT

为您的组件自动生成 stories
loading
Joe Vaughan
加入社区
6,730位开发者及更多
为什么为什么选择 Storybook组件驱动的 UI
文档指南教程更新日志遥测
社区插件参与进来博客
展示探索项目组件词汇表
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI