原文:《What Even Are React Server Components》 由Nick Telsan发布于2023年4月28日

React服务器组件(React Server Components)即RSC是一种将应用的客户端渲染和服务器端渲染之间界限模糊化的技术,它允许React组件在服务器上加载数据并仅将必要的HTML和JavaScript发送到客户端。

大约两年半之前React让我们第一次了解了SRC。最初的目标是减少网络瀑布效应 - 通过顺序发出多个服务器请求而创建的一系列网络调用。

一文读懂React中的RSC是什么?_服务器

人们已经创建了许多不同的工具和技术来避免这些加载瀑布效应。服务器端渲染(SSR)、并行获取和简单的架构更改只是您可以避免这种服务器请求瀑布效应的一些方法。但是每种方法都有一些缺点,服务器端渲染会重置客户端状态,导致用户体验不佳;并行获取可能构建速度较慢。最后,为了避免网络瀑布效应所需的架构更改往往会增加应用的维护难度。你真的想通过React Context或Redux传递所有数据吗?

RSC旨在提供一种可创建良好用户体验、构建速度快且易于维护的替代方案。

现在,我并不打算说RSC是解决所有React问题的方案。这些新颖的组件到底有多有用目前还没有定论。

相反,我想重点关注它们如何工作、何时使用它们以及如何开始使用它们。

那么,什么是RSC?

RSC是在服务器上渲的React组件。我回答了这个问题,文章结束!

好吧,那不是一个好答案。让我们深入挖掘一下。

目前,如果您创建一个React应用并使用您喜欢的构建工具进行打包,您将把应用需要的所有JS发送到客户端。这包括用于获取数据、格式化数据和渲染数据的包。这可能导致绝对庞大的JS包,其中包含客户端实际上可能不需要的内容。有一些方法可以减少这个包的大小,但您仍然会发送大量的JS。

一旦客户端收到了JS包,它可以开始渲染应用。应用可能需要一些数据,因此现在应用向后端服务器发出请求,等待响应,然后渲染出更多的数据。

一文读懂React中的RSC是什么?_HTML_02

为了完成初始渲染,客户端已经进行了至少两次请求,其中之一是从您的CDN下载一个大型JS包。使用RSC,我们通过向发送到客户端的JS和HTML中填充一些初始数据来减少这种情况。

一文读懂React中的RSC是什么?_客户端_03

而且这可以嵌套。如果您的RSC将渲染另一个具有数据要求的组件,您只需在顶级组件和其子组件中进行初始请求以加载初始数据。尽管这仍需要多个请求从前端服务器到后端服务器,但客户端只需要发起一次请求。

一文读懂React中的RSC是什么?_客户端_04

这里还有一个附加的好处。还记得我说过您正在打包应用所需的所有用于获取、格式化和渲染的JS包吗?使用RSC,您将返回最小化的JS和特殊格式化的响应以渲染一些HTML。如果客户端不需要一个包来运行,它将不会被发送过去。我们将在下一节中更详细地介绍这个。

RSC在代码中是什么样的

这可能是我最喜欢的部分 - 它们看起来就像普通的React组件!要让服务器工作,可能需要一些额外的工具(这仍然是人们正在弄清楚的问题),但就React代码而言,您只需要编写React组件。

// app.js 
import { format } from 'date-fns'

export const App = ({ userId }) => { 
    const user = getUser(userId) 
    const lastLogin = format(user.lastLogin)

    return (
        <div>
            <p>{user.name}</p>
            <p>{`Last Login: ${lastLogin}`}</p>
        </div>
    )
}

这看起来像一个简单(且不完美)的客户端React组件。它进行了一些数据获取、格式化和渲染。但实际上它是一个RSC。用户将收到的只是少量的JS和一个特殊格式的响应以渲染HTML。

这种简化的响应的一个好处是,您不需要将包发送到客户端。看上面的例子中的date-fns,它只是正确地格式化日期。客户端不需要所有这些JS代码。他们只需要格式化后的日期,因此RSC将只发送它并将包留在服务器上。

对此有一些注意事项。请注意,我说您将获得一个呈现为 HTML 的“特殊格式的响应”。您不仅仅获得 HTML,这会产生一些影响,我将在稍后的何时使用服务器组件中讨论。

另一个问题是客户端无法与RSC交互。像userState和useEffect这样的内容会被删除。只有JSON可序列化的结构才会传递给用户。幸运的是,React客户端组件是可序列化的。因此,如果需要交互性,您只需在RSC中添加一个子客户端组件。让我们将前面的示例升级一下,包括一个简单的文本字段。

// app.js
import { format } from 'date-fns'

export const App = ({userId}) => {
    const user = getUser(userId)
    const lastLogin = format(user.lastLogin)

  return (
            <div>
                <p>{user.name}</p>
                <p>{`Last Login: ${lastLogin}`}</p>
                <StatusField userId={userId} />
            </div>
	)
}

// status-field.js
"use client"

export const StatusField = ({userId}) => {
	const [status, setStatus] = React.useState('')

	const onSave = async () => {
            await saveStatus(status)
	}

	return (
		<>
                    <label htmlFor="status">Status</label>
                    <input id="status" onChange={e => setStatus(e.target.value)} />
                    <button onClick={onSave}>Save Status</button>
		</>
	)
}

那么,在这里我们做了什么?我们在RSC中添加了一个React客户端组件,以便用户可以设置一个简单的状态。现在,当用户访问我们的应用时,他们将获得之前示例中的所有初始数据,以及一个漂亮的交互式组件。

请注意,在status-field.js的顶部使用了"use client"。这是必需的,以告诉React这是一个客户端组件。除此之外,它将像您所熟悉和喜爱的任何普通React组件一样工作。

最后的两个注意事项:客户端组件无法渲染RSC,您不能将类组件用作RSC。前者似乎合理 - 客户端组件在客户端上进行渲染,因此不再直接访问服务器。后者可能会随着这些组件的继续发展而改变。

什么时候使用RSC?

RSC看起来很棒,但它们并不是解决所有React问题的万能药。您应该在什么情况下使用这些新颖的组件呢?以下是三种不适合使用RSC的情况:

  • 小而简单
    如果您的应用程序非常小而简单,您可能不会从运行前端服务器的额外复杂性中获益很多。也许您有一个不需要进行任何数据获取的应用,或者您的React应用是作为更大应用中的单个页面提供的。在这两种情况下,我建议使用静态客户端构建,并在客户端上处理可能需要的任何数据获取。
  • SEO
    目前,RSC不返回HTML,而是返回一个特殊格式的字符串,由React渲染为HTML。因此,从目前来看它们并不是最佳的SEO优化选项。如果SEO化是一个重要问题,并且您正在用React构建整个应用,那么服务器端渲染可能仍然是您最好的选择,它将为您提供一些RSC的好处。
  • 生产部署
    尽管这些组件在将近两年半之前就宣布了,但对于这些组件,仍然有很多事情需要解决。目前,它们可能不应该在除实验性应用之外的生产环境中部署。这并不意味着您不应该关注它们并在您的堆栈中尝试它们。更多的人对RSC进行测试和尝试,我们就能越早发现错误和边界情况,也越早能够在生产环境中使用它们。

不幸的是,这一点尤其可能意味着您现在不能将RSC用于任何严肃的事情,但这不应该阻止您使用它们并密切关注它们。希望我们能够在不久的将来自信地将这些部署到生产中。

那么,你应该何时使用RSC呢?

一旦它们准备好投入生产,我认为这将成为大型应用的默认 React 体验。如果您已经熟悉 React 应用的 SSR,那么这将是一个很好的替代方案,可以减少一些样板文件。现在,当您有机会尝试新技术时,您应该使用RSC。

如何开始使用RSC?

现在,您可能想知道需要在 React 中点击哪个开关才能使RSC正常工作。目前,只有 NextJS v13 的 App Router 支持RSC,不过如果您克隆 React 团队的演示应用,您也可以在没有 NextJS 的情况下使用RSC。

这主要原因是RSC需要与其他组件集成才能工作。他们需要服务器和路由器——NextJS 都提供了这两者。目前,React 团队正在帮助其他框架弄清楚如何使用SC,但我们距离这一目标可能还有很长的路要走。即便如此,NextJS 上的RSC仍处于测试阶段。

总结

希望现在您对RSC有了更好的了解。这些新的React组件易于编写和维护,并为您的应用传递数据提供了全新的方式,避免了可怕的网络瀑布效应。虽然我们离能够在生产环境中使用它们还有一段距离,但我认为在未来几年中,它们将成为React开发人员工具包中不可或缺的工具。