R

React 服务器组件注释指南

2025-05-27

React 服务器组件注释指南

React 团队本周通过演讲RFCdemo发布了 React 服务器组件。虽然内容比较多(尤其是在假期),但我还是完成了。以下是我的笔记。

2021 年 4 月更新:他们现在发布了一份建筑问答,注释如下。

TL;DR

什么是 React 服务器组件?

它们是 React 即将推出的一项实验性功能,允许你在服务器上渲染组件。这有几个影响;以下是我最看好的两个:

  • RSC 对客户端 JS 包没有任何影响,因此可以大幅减少整体包大小。尽管 RSC 仅在服务端渲染,但它们并非必须保持静态;您可以重新获取服务端组件,服务器会将更新向下传输,而不会丢失客户端组件的状态。
  • 您可以直接在服务器组件内部查询数据库,无需设置 GraphQL、API 端点或状态管理,并随着服务器端数据的变化保持查询更新。由于减少了客户端和服务器之间的往返次数和瀑布流,查询速度也更快。

注意:它们必须以扩展名命名.server.js并遵循一些限制- 主要是,它们不能使用状态、效果或 DOM API。

React 服务器组件解决了哪些问题?

它们解决了 React 应用的良好用户体验、低维护成本和快速性能问题(定义见下方演讲笔记)。对于开发者和用户来说,它们都具有诸多优势,而优势的大小取决于具体用例,因此很难完全解释清楚。

然而,这里的主要机会是大大减少生产 React 应用程序包的大小(测试显示减少了 29%,但根据您的应用程序,这个数字可能会更高),同时不牺牲现代应用程序般的用户体验(这又有多重含义 - 在下面的演示中进行了演示)。

最后,React 服务器组件可以轻松创建混合应用程序 - 让您能够在完全客户端应用程序或完全服务器端应用程序之间以及介于两者之间的所有内容之间进行选择 - 而不必随着需求的变化而进行重大重写来改变范例。

带时间戳的注释对话

我将在下面的评论中链接到时间戳,但您可以在此处查看 Dan Abramov 和 Lauren Tan 的完整演讲:

  • 1:45 Dan 介绍了 React 试图解决的主要限制:
    • 良好的用户体验- 我们希望精心设计加载状态。应该一起出现的项目必须一起出现,而不是基于异步 API 请求解析在屏幕上跳来跳去。
    • 低成本维护- 我们希望针对变化进行优化。确保代码在组件之间、客户端和服务器之间、无状态和有状态之间轻松删除和移动。为此,数据需求应尽可能委托给组件树的底层,而不是提升到最高的公共父级。
    • 快速性能——我们希望减少 JS 包的大小,避免客户端和服务器之间的往返和网络瀑布。如果组件不是动态的,我们应该在服务器上渲染一次(并获取其数据),而不是将其 JS 发送到客户端。如果可能,我们应该在客户端缓存结果。可以并行执行的请求不应该按顺序执行。
    • 过去,React 会强制你从这三个约束中选择两个。而使用 React 服务器组件,你可以同时拥有这三个约束。(Facebook 已经通过 Relay 和 GraphQL 实现了这三个约束,但并非所有人都拥有 GraphQL 后端,也不应该拥有。React 服务器组件是一种更通用且更容易采用的替代方案。)
  • 12:05 Lauren 介绍React 服务器组件演示
    • 服务器组件由新的扩展指示.server.js此处有单独的模块约定 RFC)。
    • 客户端组件也以扩展名表示.client.js。只需重命名即可将客户端组件切换为服务器组件。
    • (稍后解释)共享组件仅具有标准.js扩展。
    • (稍后解释)演示中使用了三个新的 React I/O 库:
      • react-fetch(包装了 fetch API)
      • react-pg(访问 PostgreSQL)
      • react-fs(访问文件系统)
      • 这些缓存可让您在请求数据时暂停渲染。
    • 在服务器上获取数据比从客户端获取数据要快得多,所以这是一个很好的优化
  • 16:10服务器组件对包大小没有任何影响
    • React 不会为服务器组件下载任何 JS,包括其依赖项。对于仅在服务器端使用的重度依赖项来说,这是一个很好的策略。
    • (稍后解释)JSX 的 props/children 会在到达客户端之前由服务器组件渲染。因此,你可以将共享组件传递给它们,从而节省 bundle 大小。
  • 17:25限制:服务器组件无法交互
    • useStateuseEffect,无事件处理程序(例如onClick
    • 客户端组件必须从服务器组件接收可序列化的props(例如,没有函数。JSX children/props 就可以了)
    • RFC对服务器和客户端组件的限制有更多说明。
  • 22:50 SSR 和服务器组件之间的区别
    • 您可以重新获取服务器组件树,以便 HTML 更新向下流动,而无需为重新获取的服务器组件添加任何额外的 JS。
    • 但是应用程序中具有客户端状态的部分会被保留。这是主要的区别!
    • Lauren 后来进一步解释道:“客户端 JS 应用的 SSR 只是一种假象。你在服务器上将 JS 渲染成 HTML。你将 HTML 提供给客户端,这样它看起来启动速度很快。但你仍然需要等待 JS 到达用户,然后才能进行任何交互(Hydration)。Hydration之后,SSR 就无法再次使用了——它通常只用于初始加载。我们可以根据需要多次重新获取 React 服务器组件。
    • RFC FAQ还评论了 SSR 和服务器组件之间的差异和互补性。
  • 26:45共享组件
    • 共享组件可以在服务器或客户端渲染。它们只是有一个.js扩展名。
    • 共享组件具有服务器和客户端组件的综合限制,如 RFC 中所述。
    • 在服务器上渲染的共享组件不会发送到客户端,除非它们在客户端渲染,在这种情况下它们将按需下载!
  • 30:26服务器组件可以直接查询数据库
    • 因为我们可以重新获取服务器组件树(如上所述)...
    • 一个采用动态道具并运行数据库查询的单个服务器组件(如搜索组件)可以实时更新!
    • 无需本地状态管理或 API 请求。只需更改 props 并重新获取服务器组件即可。
    • 即使您向数据库添加新项目,此组件也会保持更新!
    • 不要略过这一部分- 这是一个真正令人惊叹的时刻 - 看看我们在React Serverless Components 演示直播的 1 小时 10 分钟处实现这一点
  • 33:21悬念和缓慢的网络用户体验
    • 当网络速度较慢时,我们应该在用户等待时立即显示一些反馈。Suspense 和服务器组件可以很好地配合使用,实现这一点。
    • 方法 1:我们可以使用 Suspense 回退向用户显示有关服务器组件的一些反馈,即使在等待服务器树的其余部分呈现时(例如由于获取数据)
      • 这是可能的,因为服务器组件不呈现为 HTML,而是呈现为特殊格式以传输到客户端。
    • 方法 2:我们还可以使用 Suspense 转换,甚至在响应开始向下流动之前就立即做出响应。
  • 36:50演示要点回顾。服务器组件……
    • 36:54 对包大小没有任何影响
      • 能够决定将哪些组件放在客户端或服务器上,让您更好地控制包的大小
    • 37:42 让您直接访问后端资源
      • 您可以使用相同的范例来获取服务器和客户端的数据
      • 社区将能够创建更多 React IO 库包装器来缓存更多数据源的结果
    • 41:04 让你只加载必要的代码
      • 如果服务器组件有条件地呈现客户端组件,则服务器仅当客户端组件存在于服务器输出中时才会发送下载该客户端组件的指令
      • 这类似于动态导入,但会自动完成,无需手动加载
      • 为了实现这一点,正在与 webpack、Next.js 和 Parcel 团队一起编写集成插件。
    • 43:17 让你决定每个具体用例的权衡
      • 消除客户端和服务器之间的人为界限:
        • 将数据获取和预处理代码放在服务器上
        • 将快速交互响应代码放在客户端
        • 但他们预计大多数组件将会被共享。
        • 示例:CMS 在服务器上呈现文章(因此大多数时候可以是服务器组件),但是当您在管理面板中想要编辑它们时(因此需要在客户端呈现)
    • 44:25 提供具有服务器驱动的思维模型的现代用户体验
      • 创建现代且类似应用程序的用户界面
      • 但要像老式网页一样编写它们
      • 示例:Lauren 演示中的搜索组件是一个服务器组件,它根据客户端组件的状态重新获取,但客户端组件的状态保持不变
      • 用例:属性改变时 CSS 动画可以触发,因为现在 DOM 不会被破坏
  • 47:14回顾与展望
    • 它是可选的,仍处于研发阶段(缺少核心 API,例如服务器组件路由器,并且需要最终确定并发模式),并且将对所有人开放,即使那些不使用 Next.js 的人也是如此
    • FB 的生产测试已将捆绑包大小减少了 29%
    • 不要制作相关课程或将其投入生产。只需使用演示版本即可。

您还可以在此处查看 Twitter 帖子上的更多评论

演示演练

React 团队还发布了演讲中展示的演示:https://github.com/reactjs/server-components-demo/

不过,演示版中有一些复杂的设置步骤,还有很多东西需要尝试。我最近花了两个小时仔细检查了每个部分,还加了时间戳。

您还可以将服务器组件放入无服务器功能中,正如我今天发现的,这很困难

个人感想

这是React 一段非常漫长旅程的结束的开始,这段旅程可以追溯到 2014 年,当时Jordan Walke 首次提到异步渲染( Concurrent React的旧名称)是可能的。

我与 React Suspense 的缘分始于 Dan Abramov 在 2018 年冰岛 JSConf 大会上介绍 React Suspense 之后。它震撼了所有人,彻底颠覆了我之前关于 React 应用应该如何编写的认知。我熬夜写了那个 demo 的演示。当时我还没有 React 项目,但在接下来的两年半里,我一直在关注这个想法。

我怀疑,那些经历过这段旅程的人和今天才开始关注的人对 React 服务器组件的看法会完全不同。就像你本来很喜欢一部电影,结果你那势利讨厌的朋友却对你说“书更好”

对我来说,“这本书”提供了更多的背景信息,虽然今天看来已经无关紧要,但却让我更加了解我们是如何走到今天的,以及事情在幕后是如何运作的。

以下是我们在谈话或演示中没有讨论过的所有术语(因为它们是实施细节或与今天无关):

相反,今天我们只提到了一个概念——服务器组件。如果你仔细观察,甚至会发现它根本没有 API

总结

由于 React 团队多年来一直在传达他们的想法,因此 React Server Components 的主要好处与我在写作演讲中所评论的相同

给定一个基准 React 运行时,React Server Components 可以让你:

  • 通过让您无需发送不必要的 JS(即所谓的消失的应用程序,大大减少了实际应用程序的大小
  • 让客户端和服务器组件在同一棵树中无缝运行,这样当需求发生变化时您不必改变范例。
  • 通过跳过客户端-服务器瀑布并直接访问后端资源,可以更快、更轻松地提供数据。
  • 即使在加载服务器组件时也要保留客户端状态和 DOM 节点,以便您甚至可以进行页面转换、CSS 动画和持久输入焦点。

对于 React 开发人员来说,这是一个绝佳的新机遇领域,其生态系统预计将在 2021 年实现巨大增长。我确信它也将引发其他框架的类似努力(因为并发模式的案例首次超越了时间分片和延迟加载组件)。

相关阅读

随着时间的推移,我将用其他精选作品来更新此内容。

2021年建筑问答笔记

  • 5:15还剩下什么?
    • 服务器组件的一切都与服务器渲染和客户端相关,特别是与 Suspense 的工作方式相关。因此,我们的服务器渲染故事中缺少了一些内容:
    • 流式服务器渲染器。 “我们目前正在开发一种新的流式服务器渲染器实现,它有一些有趣的新功能,但我们计划特别集成来自服务器组件的数据流,以便您能够使用服务器组件将数据渲染到 HTML 中。”
    • 智能打包策略。“我们缺少的另一个部分是,我们希望确保在打包输出时,服务器组件能够隐式地提供内置的细粒度打包拆分功能,但我们也希望确保有一个特别实用的打包策略,而不是仅仅让策略倒退,因为如果将打包拆分成太小的部分,结果可能比根本不拆分更糟糕。因此,我们正在努力至少提供一个 Webpack 插件的原型,或者理想情况下是完整的实现,它将为您提供相当不错的开箱即用体验。还有其他方法可以实现这一点,我很想看看社区会提出哪些不同类型的打包策略。但我们希望至少能够在这个领域提出我们迄今为止最好的想法。”
    • 数据获取。“还有一个问题,那就是它如何与实际启动数据获取的 API 相连接,比如路由、分页或其他类似的功能。我们对此并没有什么特别的看法,但有些模式效果很好,有些模式效果不好,所以我们至少想提供一个原型和一个演示,展示如何解决这些问题。”
  • 7:50什么时候?
    • 希望今年……我们正在努力发布客户端方面的候选版本,我们希望推出 React 18 版本的新版本以及服务器组件 MVP,并且可能推出一些流渲染作为预览包,希望今年能够推出。
  • 8:30 服务器和客户端之间共享 GraphQL 缓存?
    • Jae:“正如您所说,它与服务器和客户端渲染以及数据获取之间的联系非常紧密。我们的应用是基于 GraphQL 构建的,特别是 Apollo GraphQL,这意味着它基于 GraphQL 缓存构建,该缓存在服务器渲染期间预热,然后传输到客户端,然后在用户会话过程中,缓存会被新的查询和变更修改。您是如何看待这类事情的?例如,目前 GraphQL 缓存在服务器和客户端之间共享。您是否计划开发一些与服务器组件兼容的东西?或者说,生态系统以及我们开发者是否需要重新思考如何以这种方式与数据交互?”
    • Joe:“所以,我们将其视为使用 GraphQL 或其他数据获取方法开发应用程序的一种进步。
      • 第一步是从非 Suspense 数据获取过渡到使用 Suspense。我们的想法是,不再使用 useEffect 或其他方式获取数据,而是切换到基于 Suspense 的数据获取。为了使其与服务器渲染兼容,需要 Sebastian 提到的 Suspense 流式感知服务器渲染的一些功能。这算是第一步,它能让你基本保留目前应用中使用的模式,并继续进行服务器端渲染。
      • 但问题是,这样可以让你能够在树中的多个点进行抓取,并让所有这些查询都发生在服务器上,同时避免初始页面加载时的往返。但这并不能真正解决的问题在于,现在你在客户端,想要进行页面转换之类的操作,你又会回到这样的状态:你开始渲染,在渲染应用时可能会遇到多个查询,而这些查询可能会导致瀑布流,所以我们认为服务器组件可以提供帮助。
      • 但这有点像迁移到 Suspense 进行数据获取之后的第二阶段。至于 GraphQL 或其他类型的规范化数据存储如何融入服务器组件世界这个更广泛的问题,我们预计它们不会真正消失。应用中的某些交互部分需要客户端的数据一致性,我认为,继续使用我们目前都在使用的现有方法来构建这些部分是有意义的。
      • 有很多好的方法:GraphQL、REST 和各种不同的数据库。我认为真正需要改变的是,对于应用程序中那些适合转换为服务器组件的部分,你开始考虑进一步区分哪些是状态,哪些是规范的服务器数据。
    • Seb:总体策略就像你需要一个 Suspense……所有这些都建立在 Suspense API 之上,所以无论如何你都必须构建它。我们期望目前存在的许多库都能提供某种开箱即用的支持,这样你就可以采用服务器组件的方法,包括在组件中共置数据提取,而无需初始加载时出现瀑布流。但是,如果这种共置或转换与你目前的做法相比可能会导致性能下降,如果你有一个非常优化的解决方案,那么服务器组件可能就是解决方案。所以有时我怀疑你实际上会想要等到你同时拥有这两个部分后再推出它,即使这是一个两步走的采用过程,这样你就不会在此期间整体性能下降。
    • Jae:我想只是为了确保我的理解正确,我们正在查看的服务器组件不会根据缓存中的更新而更新,因此,我们正在查看的服务器组件是使用规范数据(例如来自 CMS 或类似数据)呈现的东西,但不是交互性的一部分,这些东西将是客户端组件。
    • Joe:我认为你在服务器组件中获取的数据不一定来自 GraphQL,也可能来自客户端组件获取数据的同一数据源。但通常情况下,在你获取的数据中,有些数据会以不同的规律变化。就拿 Facebook 的例子来说,故事的文本可能不会经常变化,尤其是你还没写过的帖子。只有在你重新获取整个故事时,数据才会发生变化,这时你需要访问服务器,这样你就可以重新获取服务器组件的实际输出。所以,数据类型各不相同,有些数据变化频率较低,因此你可以重新获取完整的数据,而当你重新获取数据时,你只需重新获取服务器组件,而不是获取数据然后重新渲染客户端。所以,这有点像数据变化率的问题,以及它必须保持多大的一致性。
    • Seb:可以这样理解:你先编写一个客户端组件,然后如果发现该组件没有任何状态或效果,就可以将其转换为服务器组件。但你不必费尽心思转换整个组件树或整个子树。你也可以只是在转换过程中转换单个组件。因此,组件树中的一些组件可能正在从客户端获取数据,或者作为初始服务器渲染的一部分,而另一些组件可能是嵌入在组件树中的服务器组件。
    • 安德鲁:这里的主题是,有些组件不会频繁更新,而其他组件则具有高度交互性并具有更多本地状态,例如 UI 状态......也许它从服务器接收数据,但你可以从父组件传递它。
      • 所以,如今很多已经在使用 Apollo 或 Relay 等数据框架的人,可能已经大致按照这种模式编写代码了:高度交互的客户端组件与真正用于管理数据并向下传递的数据组件之间存在某种分离。这种模式与服务器组件配合得非常好。
      • 但有些人可能只是把所有东西都扔进同一种状态源,比如商店或类似的东西,而这些模式可能需要更多的工作才能迁移到这个世界,你需要更仔细地思考你拥有什么类型的数据。
  • 16:50 流式服务器渲染
    • Yen-Wei:我特别好奇地想了解您如何看待客户端组件和服务器组件、客户端组件和服务器渲染之间的互操作。
    • Seb:客户端组件和服务器渲染,两者融为一体。是的,对于服务器渲染,我们在构建服务器渲染时会参考 Suspensey 的方法。因此,这与服务器组件是解耦的:即使没有服务器组件,它仍然会存在。例如,如果一个数据源比另一个慢,这种方法允许你以流式传输 HTML 块,这样你就可以看到 UI 逐渐呈现流式传输。这在某种程度上与整个 Suspense 方法紧密相关。但你会发现,每个客户端组件都可以转换为服务器组件,之后发生的情况与客户端类似。我认为服务器渲染器更像是一个模拟的客户端环境,其中服务器渲染器接收原始请求。但是它可以像客户端请求附加数据一样请求数据,并且该数据可以是服务器组件的子树,然后将其输入到充当客户端的服务器渲染器中,然后输出生成的 HTML,然后还将服务器组件输出作为 JSON 数据嵌入到 HTML 中。
    • Seb:一个关键的区别是,当前的方法倾向于以最原始的形式嵌入数据,因此,如果您在服务器上获取 REST API,您可能会在 HTML 中嵌入 REST 响应以供 hydration 使用。但在服务器组件方法中,我们将服务器组件的结果嵌入到 JSON 中,这意味着您将在输出中获得某种非规范化和处理过的数据,这些数据有时可能更大但渲染速度更快,有时则更小,因为您只加载该组件实际需要的数据,而不是整个 REST 响应。
    • Andrew:我个人有时觉得很困惑,即使我知道所有组件之间的区别,只是因为命名本身就很混乱。现在人们会想“嗯,它叫服务器组件,我已经有了一个叫服务器渲染器的东西,那么服务器渲染器肯定在渲染服务器组件”,但事实并非如此。输出 HTML 的东西,也就是我们今天在服务器组件出现之前传统上认为的服务器渲染的东西——在这个新的架构中,它实际上并不渲染服务器组件。它只渲染客户端组件,这有点让人费解。它实际上已经接收了——你看,我现在甚至都不知道该用什么词了,但有一个运行服务器组件的层,它会将数据发送到客户端渲染器,然后就有两种客户端渲染器:一种在浏览器中运行,一种在服务器上运行。我不知道我是否完全解释了这一点,但输出 HTML 的代码和获取数据并生成流式输出(然后可以将其转换为 HTML)的代码之间存在区别。希望这能有所帮助。
    • Yen-Wei:是的,所以我想在这种情况下,服务器渲染器基本上就像模拟的客户端运行时,对吧?那么我想,这是否也意味着客户端组件仅在客户端上运行的假设在那个世界中是错误的?
    • Andrew:是的,默认情况下,客户端组件确实在 Node 服务器环境中运行并输出初始 HTML,或者它们在浏览器中运行。对于某些组件,有一种用例可能您甚至不想尝试在服务器渲染器上渲染初始 HTML - 因此我们正在考虑一种 API,您可以直接退出并说,不必费心尝试在服务器上渲染这棵树,我们会在客户端上选择它,这是一个非常好的功能,因为它可以让您精细地控制哪些东西可以在两种环境中运行,哪些东西不能。但是,是的,总的来说你是对的:这个世界上的客户端组件并不一定意味着您可以像访问窗口和所有这些仅限浏览器的 API 一样。如果您想充分利用流式 HTML 生成,那么同样的限制也适用。
    • Seb:就命名而言,我认为还有其他一些有趣的理解方式,因为服务器组件实际上是在充分利用服务器的优势:比如靠近数据、节省资源,并且代码已经加载完毕。而服务器渲染更像是一个魔术,我认为这是一个很好的理解方式,因为它只是渲染一个快照,用户在与其交互之前可以看到它。但希望在渐进式混合模式下,当你尝试与其交互时,不会有任何感觉上的差异,但这才是服务器渲染的真正目的。它提供了一个快速初始快照的魔术。这类似于在 iOS 应用上,你可以看到启动时之前存在的像素快照,然后它才真正启动。这是一种类似的技巧,让它感觉启动速度很快,而服务器组件实际上是一种永久性的方法,它有助于在应用中进一步导航,并永久避免加载代码。
    • Andrew:是的,我喜欢“快照”这个名字……如果有人对命名有什么好的建议,我们很乐意听取。我喜欢“快照”这个名字,因为它让我想起了 V8 的快照。
    • Lauren:我个人一直在用的一个术语是“引导”,有点像引导页面,这样 React 就可以接管并实际执行它需要做的事情。没错,服务器端渲染提供了初始框架,让你可以在此基础上进行实际操作。
    • Jae:是的,在 FindMyPast 我们经常称之为“预渲染”,因为服务器渲染会让人联想到 ASP.NET MVC 类型的应用程序。它实际上并不是在做这件事,所以我们开始称之为预渲染,因为它是一种优化。
  • 25:00 服务器组件性能
    • Jae:我第一次和一位前端平台团队负责人的同事讨论服务器组件时,他立即关注的一点就是我们的服务器渲染(服务器预渲染)——它已经是我们技术栈中相当耗费资源的部分,而且每个会话只进行一次预渲染。他当时在想,这会是什么样子?这个服务器组件提供程序的性能特征会是怎样?它在用户会话的整个生命周期内,无论是在连接方面,还是在使用服务器组件进行处理方面,都需要做更多的工作。对于服务器组件的结果,即使对于不同的用户,甚至对于同一个用户,在整个会话中反复请求,也可能是相同的,那么是否会有一些内置的优化措施,比如缓存或记忆这些结果?
    • Seb:就我们目前在 Facebook 的经验来看,它对资源的占用并不高。我认为这部分是由于 REST API、数据处理或 GraphQL 端点的资源占用情况。另一方面,服务器组件的后续请求不一定像初始服务器渲染那样耗费资源,因为它只涉及服务器组件,不涉及客户端组件,而且它还是一个子树。因此,它具备重新获取子树的能力,但这确实是我们关心的问题,我们希望通过重新获取子树的能力来解决,而不是在刷新页面时重新获取整个页面的所有数据。
    • Seb:说到缓存,我们对以各种形式缓存子树的能力有一些想法。缓存总是比较棘手,因为你必须确保能够正确地使其失效。但这也与上下文相关,因为我们有能力获取子树(就像我刚才提到的),如果你想保留这种能力,那么我们也有能力将这些子树的响应缓存在任何特定的树中。但我们必须对输入进行编码,例如,如果你正在进行 HTTP 获取或文件读取,那么除了初始 props 或你读取的所有数据之外,所有输入都需要参与其中,并让我们有一种使其失效的方法——无论是时间戳、文件监视器还是订阅方式。因此,我们还没有完全弄清楚用于该失效的 API 是什么,而且事后添加可能会比较棘手,所以我们仍然在弄清楚它是否应该从一开始就成为数据提取 API 合同的一部分,以便您以后不会失去这种能力,或者它是您以后可以逐步采用的东西。
    • Lauren:我想补充一点,在客户端层面,服务器组件的响应也是可缓存的,这取决于产品的需求。例如,如果您的应用程序中有一个部分是静态的,并且驱动该部分的数据变化率很高,那么这些组件(比如导航栏)如果这些初始服务器组件的响应被缓存,您就不必重新渲染服务器组件。这些服务器组件的响应并没有什么特别之处或独特之处,因此不容易缓存。例如,在 Relay 中,我们确实会缓存服务器组件的响应,如果数据没有变化,我们基本上会利用这些响应。我们不需要重新获取服务器组件,而是直接从 Relay 存储中恢复它。
    • Joe:需要补充一点,您提到服务器端渲染(也就是您所说的预渲染)目前非常耗资源。我认为需要注意的是,对于某些库来说,目前使用数据获取和 useEffect 进行服务器渲染的唯一方法是……生态系统中的某些库会多次遍历渲染树来确定 UI 需要哪些数据。一旦缓存预热完毕,它们就可以进行完整的渲染,但这显然需要多次遍历渲染树。使用 Relay 时我们不会遇到这种情况,因为我们实际上是预先获取所有数据,而服务器组件的优势之一就是它使这项工作更容易完成。因此,使用服务器组件可以更轻松地构建应用程序,这样您就可以避免为了确定渲染内容而一次又一次地遍历渲染树。此外,新的流式 Suspensey 服务器渲染实际上也可以继续工作。有了 Suspense 的抓取功能,我们可以从上次中断的地方继续工作,而不必从头开始。所以我认为,即使最初看到今天的预渲染成本可能很高,但情况也可能会改变,对吧?这不仅仅是“哦,我们又增加了工作量”,它实际上可能会让你已经在做的所有工作都变得更高效。
    • Seb:我有一个问题:您的 GraphQL 在哪里实现的,是 JavaScript 服务还是其他语言?
    • Jae:是的,GraphQL 主要用 JavaScript 编写,但它是一个分布式图 —— 因此我们有一个中央 Node.js 服务器,它将针对模式不同部分的不同请求代理到用各种语言(但主要是 Node)编写的后端服务。
    • Seb:我想我之所以问这个问题,是因为运行时本身就有一些开销。例如,如果你现在有一个 REST API,并且该 REST API 是用 Node 构建的,那么你可以将服务器组件作为附加层添加到同一个运行时的末尾。同样,如果你在 Node 中或者甚至在前端有一个 GraphQL 实现,那么你可以在同一服务的末尾添加服务器组件,这样可以分摊一点总体成本,因为你使用同一套服务来处理数据和服务器组件,本质上它只是一个数据处理管道。
  • 33:10 缓存服务器组件响应
    • Yen-Wei:是的,所以我认为这有点像上一个问题的延续。我们讨论了服务器组件响应的缓存,我有点好奇——你知道,我们目前的做法是将结果数据缓存在客户端存储或提供程序中。我们在应用中使用 Redux。我想知道——说到 Relay 存储缓存服务器组件的响应——这是 React 本身会考虑的事情,还是仅仅取决于用户层和产品的需求?
    • Seb:是的,我只是想把它和我在简介中提到的缺失部分联系起来。这里有一个关于路由和触发获取的部分,其中也包括缓存。我们有一些想法,关于如何在不使用任何额外库的情况下实现这一点,就像你能做的最简单的那样,你可以使用缓存——React 内置了一个 Cache 原语——它实际上既用于在服务器上保存你在服务器上使用的响应,也用于在客户端保存响应。但是,Cache 也用于你可能在客户端获取的任何临时数据,例如,你可能希望在其中包含图片以支持类似 Suspensey 图片技术,或者你可能希望有一个临时的客户端请求也进入同一个 Cache。这就是基本方法,我们对它的根源有一些看法——它的根源在于——React 中的某些子树具有生命周期,而该生命周期控制着缓存。但你也可以将它构建到现有的、更全局的缓存中,例如 Relay。
    • Andrew:如果你曾经使用过 Suspense,比如我们之前发布的 Suspense 预览版,你会发现我们显然没有解决缓存问题。我们只是提供了一些关于如何实现用户空间缓存的方案,并在如何执行失效操作或如何确定树的哪些部分需要保持一致等问题上,设置了一个巨大的待办事项。所以 Seb 提到的 API 就是我们现在要提出更多意见的地方。所以,如果你正在使用 Suspense,React 会提供统一的内置缓存 API,不同的框架可以将其连接到这个 API。每个框架对于如何填充缓存可能有不同的实现,但会有一个统一的模式,告诉你应该如何使缓存失效,如何决定树的哪些部分需要重新获取,或者在服务器发生变更或其他情况后,树的哪些部分需要更新。在此之上肯定会有额外的层,像 Relay 这样的框架会对它们有特定的实现意见,但对于缓存实际存在的位置的最底层,我们将为其提供一个 API。
    • Seb:为了解释一下它的目的——这有点深奥——缓存的目的是为子树提供一致性。所以,假设你正在为服务器组件执行获取操作,但你的服务器组件可以叠加在客户端组件中,而客户端组件也可能在大约同一时间执行获取操作,从而填充同一个缓存。这样做的目的是,你可以将所有这些作为一个单元失效,这样你就会收到一个新的服务器请求来获取新数据,但你也会收到客户端请求获取同一子树的新数据。所有这些都与客户端 React 中的子树绑定在一起。
  • 38:10 服务器组件中的错误处理
    • Jae:所以我想知道您是怎么想的,如果服务器组件出现错误,如果提供服务器组件的服务不可用,客户端是否有办法说“好吧,如果你不能获取子树,就同时显示这个”或者如果有一些子树无法从服务器组件获取,应用程序就无法继续渲染?
    • Seb:那么我可以先谈谈通用机制,以及如何将其融入最佳实践中。有几个地方可能会发生错误。
      • React 本身之外的运行时也可能发生错误。这些错误更多地取决于基础架构元框架的处理。
      • 然后,作为网络的一部分,可能会发生错误,也许您根本没有得到响应,或者您得到了部分响应但出现了连接错误。
      • 然后服务器组件中可能会发生错误。
    • Seb:因此,当服务器上的服务器组件中出现故意抛出的错误时,会发生两件事。
      • 第一,你需要在服务器上记录日志,这样你就可以追踪它。即使它们最终没有到达客户端,你仍然需要知道发生了某种错误。
      • 另一部分是它被嵌入到响应中。然后,该组件在客户端树中抽象地渲染,并重新抛出错误,以便客户端的错误边界能够处理它。
      • 如果发生错误,例如,您只收到了部分响应,而不是全部,或者根本没有收到响应,客户端运行时都会针对树中所有尚未渲染的部分抛出错误。因此,如果您渲染了一部分——记住,这是一个流式协议,因此您可以部分渲染已有的数据,但错误发生在尚未渲染的地方——那么距离这些位置最近的错误边界就是处理错误的地方。然后,由错误边界决定如何处理,是应该显示错误还是应该重试该请求。
    • Jae:是的,这听起来非常灵活,它将为我们提供针对所有不同错误处理情况的多种选择,而且听起来比现在服务器上的错误、客户端上的错误更容易。
    • Seb:是的,这个领域有一点棘手,那就是你可能有一个通用的错误边界,它只会为所有错误呈现一条错误消息。但在这个世界上,如果你从未使用像 I/O 错误这样的错误作为错误边界抛出,那么这些边界可能无法意识到它们应该将 I/O 错误视为特殊情况,或者在 I/O 错误时重新抛出。所以现在有点棘手,错误边界必须意识到 I/O 错误是特殊情况,这样它才能知道委托这些错误或知道自己处理它。否则,如果你有一个处理 I/O 错误的深层边界,它可能不会重新获取,而如果它能够通过该错误边界冒泡,它将获得知道如何重新获取它的父级。所以这仍然有点棘手,但我认为它仍然非常灵活。
  • 43:05 分页
    • Yen-wei:我们好奇的一件事是——我们的很多页面基本上都是巨大的信息流——所以我们经常考虑分页。我很好奇,在服务器组件、分页和获取后续页面方面,它会是什么样子。
    • Joe:是的,这个问题问得很好。说实话,我们还不确定。我们考虑过这个问题,也探索过,但目前,比如说,我们在分页功能中使用 Relay,也就是在单个条目上使用服务器组件,而且我认为我们还没有在类似 Feed 的场景中使用服务器组件。但如果我们真的使用了,很可能是外部使用 Relay,内部使用服务器组件,我认为我们的想法是逐步探索这个领域。
    • Joe:我认为即使使用 Relay 也存在一个挑战,我们仍在评估如何正确地使用 Suspense 进行流式分页,在这种情况下,我们希望新项目从服务器到达并进行增量渲染。但显然,如果集成 Suspense,即使第二个项目可能先准备好,你也会先显示第一个项目,然后再显示后续项目,对吧?所以它必须与 SuspenseList 集成。所以,是的,这似乎是一个非答案问题,其他人可能有更多想法,但这就是我们目前的现状,我们已知的、真正有效的方法。
    • Seb:我认为实际上已知的内容比看起来的要多,因为有很多可能的版本我们认为不会奏效。我们没有确切的 API,但我们认为结构大致相同。我们探索了各种形式,例如,如果你重新获取整个页面,并告诉服务器现在在列表中包含更多内容,这是一种方法。但我们认为最终会采用的方法,也可能是最直观的,就是将列表中的每个项目想象成它自己的子树,我们将能够只重新获取一个子树,并从你上次中断的位置继续获取上下文。
    • Seb:所以这个想法基本上就是,你有一个客户端组件来管理列表,它发送一个“给我这个额外项目列表”的请求,然后服务器渲染这些项目,或者服务器组件渲染这些项目,然后你得到返回的结果,这就是你在列表末尾渲染的内容。这实际上就是我们在 Relay 中所做的。在设计这个 API 的具体方式上有一些细微的差别,但我认为这是通用的原则,而这种特定方法的部分原因是页面本身是有状态的,因为你在列表中的位置是一个客户端概念。如果你只是重新获取——这在 Facebook 上尤其如此,因为每次你刷新新闻源时,你都会得到一个完全不同的顺序——它没有任何固有的顺序。
    • 由于底层数据可能会发生变化,列表也会随时间变化。因此,我们实际上并不想在请求中重新获取列表本身,我们只想添加一个额外的页面,然后获取该页面并将其添加到我们已有的数据中。为此,我们需要能够获取上下文。但它应该是哪个上下文——应该是最新数据的上下文,还是应该是渲染外部列表时所用的上下文?我们认为它应该是渲染外部列表时的上下文。所以我们得出了很多结论,最终的结果看起来很像 Relay 分页,所以我会将其视为一种启发。
  • 47:53 测试
    • Jae:所以,我们正在考虑的另一个所有这些功能都必须运行的环境是测试。目前,我们有不少测试是针对 jsdom 运行 React 的,这些测试运行速度比 Cypress 等实际运行浏览器的端到端测试更快。所以我一直在想,服务器组件是如何融入其中的。服务器组件提供程序是否可以作为运行测试的同一进程的一部分在本地运行,或者您是如何设想的?
    • Lauren:目前,我们的原型确实进行了测试,但这些测试基本上都是端到端测试,在测试中我们确实运行了服务器组件的渲染基础设施。我认为单元测试的概念至少对我来说还不是特别清晰,所以其他人可能会有自己的想法。不过,我们确实运行了端到端测试,因此我们可以看到渲染服务器组件的完整端到端流程,然后将其进行初始加载,以及端到端测试中可能出现的任何交互,这些都可以在那里测试。因此,假设你也能运行你的服务器组件渲染基础设施,它应该可以插入到现有的端到端框架中。但服务器组件的有趣之处在于,我们正在探索一些途径,比如我们目前正在研究如何在不同的环境中运行服务器组件,比如不在服务器上,比如在 Worker 或类似的环境中,这可能有助于单元测试。
    • Andrew:单元测试有很多种类型。我不太明白大家说的是什么,我觉得通常是指某个层级的某些部分被模拟或存根了。
      • 因此,如果您想要对需要服务器组件数据的客户端组件进行单元测试,那么这可能与今天非常相似,您不是在服务器组件内部渲染它,而是在为其提供道具的其他东西内部渲染它。
      • 如果你想对服务器组件本身进行单元测试,因为服务器组件可以渲染 UI,我可能会这样做:模拟请求环境并生成实际输出。然后将其输入到我们称之为预渲染 API 的地方。然后像对客户端组件进行单元测试一样,对 React 输出进行断言。你可能不应该断言它输出的实际数据格式,所以我想这取决于你试图测试的是堆栈的哪一层或哪一部分。
    • 但即使是我称之为单元测试的东西,我通常也觉得尽可能保持“端到端”测试更有价值。所以,是的,如果合理的话,我可能不建议对除了最终树输出之外的任何内容进行断言。
    • Seb:我还要补充一点,很多我们称之为服务器组件的东西实际上是共享组件,可以在客户端或服务器上运行。如果你只想测试逻辑而不是集成,一种方法就是像现在测试它们一样,直接将它们渲染为客户端。不过,我确实认为,我们的观察是,朝着端到端的方向发展,无论是像 jsdom 环境那样模拟端到端,还是更丰富的全浏览器端到端测试,似乎都是很多事情正在发展的方向,因为它确实简化了许多异步行为(比如 Promises)的测试。
  • 53:12 测试子树
    • Jae:我猜想端到端测试,尤其是像完整浏览器那样,会消除很多设置环境的复杂性,但在性能和可以编写多少个测试并保证它们高效运行之间仍然存在权衡。所以,是的,具体来说,我当时想,是的,但我们只想测试 jsdom 中的一个子树,尤其是如果这个子树同时包含服务器组件和客户端组件会发生什么?这可以在 Jest 的进程内运行吗?还是说,你必须启动一个单独的服务器工作进程来执行服务器组件的工作?……
    • Seb:这个问题问得好,因为这种环境下的基础设施有点棘手,因为我们对导入的处理方式做了特殊处理,所以通常服务器无法……嗯,理想情况下,服务器组件渲染器应该设置为一个独立的进程,即使是“预渲染器”,也就是所谓的“引导”渲染器,也可以。但只要它们构建为独立的模块系统,就可以在同一个环境中运行它们。例如,很多服务器渲染的生产环境在 Node 中加载之前会使用 Webpack 打包。由于 Webpack 有自己的模块系统和图表,你可以将两者放在同一个进程中。
    • Sev:但是如果您能够将其作为客户端组件运行,它的行为或多或少会类似。它并不完全相同,但将客户端组件放在客户端树中服务器组件所在的位置或多或少是相同的,这就是想法。您在开始时提到,发布之前您正在考虑的事情之一是 webpack 插件。我想知道是否有计划为非 webpack 捆绑提供一流的支持,以及例如在服务器上捆绑是否实际上是服务器和客户端组件的要求。是的,所以我们首先做 webpack,但我们希望为任何可以为客户端提供良好开箱即用体验的捆绑器提供一流的捆绑支持。
    • 这里存在一些限制,特别是现在运行时与 webpack 耦合的原因,因为我们有点依赖一些内部机制来同步提取和延迟加载模块,即使它们已经加载并提前预加载。因此,为了真正获得理想的性能,我们依赖很多这些功能,这些功能不一定是标准 API 的一部分,但有更多其他捆绑软件支持相同的功能,我们绝对可以支持它们。另一部分只是获取捆绑策略,我们并不真正知道它究竟如何工作。但肯定可以为其他人构建一些东西,如果它是一个高质量的实现,我们甚至可以将其作为一流的包来维护,我们很乐意为此提供帮助。
    • 问题的另一部分是服务器组件(服务器部分)是否需要捆绑。这些组件并非必须作为开发的一部分进行捆绑,我认为现在生态系统正在发生重大转变,试图探索其他可以加快开发体验的开发方式。例如,不进行捆绑。但我们也认为,理想的开发人员调试体验实际上可能是在 Service Worker 中运行服务器部分,这可能需要某种捆绑,或者至少是部分捆绑或部分编译才能获取 JSX 之类的代码。但是,即使是我们的演示也没有真正捆绑服务器,我认为这实际上是它最大的缺失。
    • 我认为这样做是理想的,但并非必须的,原因有两点。首先,在 Node 环境中运行 bundle 通常速度会更快一些。
    • 但另一方面,我们可能希望使用在服务器打包过程中确定的图表来确定客户端组件的最佳打包策略。我知道 Webpack 的 Tobias 有一些想法,甚至设计了一个在服务器包和客户端包之间共享的图表,以便它能够获取这些信息。但这实际上取决于你的打包策略。在 Facebook,我们使用数据驱动的打包方法,我们会查看之前的访问情况,并尝试使用统计模型来确定如何对某些客户端组件进行最佳分组。
    • 但如果没有这些信息,你就必须从静态构建中获取尽可能多的信息,而这些信息中的很多都依赖于服务器图。例如,如果你有一个服务器组件,它总是会拉取这三个客户端组件,那么你希望在构建客户端组件时能够知道这一点,以便知道如何对它们进行分组。
    • 但你不必这样做,因为你可以将所有客户端组件构建为一个单独的图,并将它们全部视为入口点,但这样一来,你就没有太多关于如何分组最佳块的信息了。这里也有一个折衷方案,你可以使用一些实际上不运行服务器捆绑的东西,而只是将其用作分析,并将其输入到客户端的捆绑中。但我认为,我们想要构建的第一个方法,也就是缺失的部分,是一种统一的方法,其开箱即用的体验是,你先构建服务器,然后将其作为输入来构建客户端。
  • 1:00:30 设置服务器组件的样式
    • Jae:我也在思考 CSS,既然服务器组件可以渲染 UI,那么当服务器组件 UI 被获取时,CSS 如何在正确的时间到达客户端呢?无论是通过 CSS-in-JS 还是 CSS 模块。尤其是考虑到这些服务器组件可能……运行它们的代码可能永远不会下载到客户端,那么客户端如何知道下载正确的 CSS 并将其放在正确的位置呢?
    • 塞布:我们观察到基本上有三种不同的策略。
      • 我们目前在 Facebook 使用的策略基本上是静态分析,我们分析文件,然后创建 CSS 包,它们基本上是一个包含所有 CSS 的包,在这种情况下,您只需确保分析能够遍历这些文件,这样它就不会只遍历客户端,它必须遍历 — — 这也与上一个问题相关,对吧 — — 您必须有某种东西可以遍历服务器文件以找到其中的 CSS。
      • 另一种策略更像是无需插件的 Webpack 开箱即用体验,你可以将 CSS 文件作为模块的一部分导入。在这种情况下,这意味着如果你加载该文件,CSS 文件也会随之加载。但 CSS 文件和组件之间没有明确的联系,你只需导入它,它就在那里了。这需要一些特殊考虑,因为该模块不会被拉入 Webpack 客户端包中,因此依赖关系图中不会出现。这是我们可能想要添加到官方 Webpack 插件中的一部分,因为这是 Webpack 的基本功能,插件,因为这是 Webpack 的基本功能,我们必须做一些巧妙的事情,比如转换文件以便注入调用,这样我们就知道这个文件以某种方式与这个组件相关联。
      • 但我认为第三种选择更为常见,即无论静态注入还是运行时注入,组件中都会有一些东西决定这个类名与组件关联,需要注入。要么需要下载依赖项,要么需要动态注入。当然,你也可以在用户空间的第三方代码中做到这一点,但我认为我们实际上应该为这种情况公开一个特定的 API,这样你就可以说“这是我想要与这个输出关联的类”。如果这个输出包含在子树的这一部分中,那么服务器组件的响应中就会有一些与之相关的元数据,然后客户端可以注入这些元数据来加载相应的 CSS 或将 CSS 包含在服务器渲染器中。但我认为这将是一个缺失的部分,我们必须在它真正发挥作用之前将其添加进去。
      • Lauren:我只想快速补充一点,不是专门针对 CSS,但我认为这也属于这类问题:一些过去发生在客户端的副作用现在发生在服务器上,所以你需要某种方法来跟踪所有发生的副作用——无论是日志记录、抛出错误,还是正在使用的 CSS-in-JS——然后根据产品的需求在客户端重现这些副作用。比如在发生错误时,我们会重新抛出错误;或者在 CSS 的情况下,你可能需要请求该 CSS,或者向这些组件注入一些 CSS 类。所以我认为这和我们正在研究的问题非常相似。
      • Seb:是的,我们在 Relay 上也遇到过类似的问题,对吧?我们想发出数据依赖关系,因为我们从服务器得知,我们需要这些数据作为即将渲染的客户端组件的一部分。
    • 1:05:30 准备服务器组件
      • Yen-Wei:我们现在应该做些什么来让迁移变得更容易?显然,我们希望能够在服务器组件发布后立即采用它。我们是否应该在我们自己的代码库中优先考虑某些事情,以最终帮助实现迁移?
      • Andrew:所以这涉及多个层面。我们在本次聊天开始时就提到,它依赖于一些并发渲染功能,我们之前也讨论过这个问题。我们的下一个 React 版本,React 18,将支持并发渲染。并非所有服务器组件的功能都依赖于 100% 兼容并发模式。但只要开始添加 Suspense 边界并在应用程序的某些部分使用服务器组件,你就相当于选择将这些子树纳入一定程度的并发行为。所以我们对此进行了深入思考,我们的粗略策略是,你将应用程序升级到 React 18,在切换到新的根 API 方面,基本上不会有任何变化。我们会移除一些非常微妙的遗留问题,但所有东西仍然是同步的。然后,随着你逐步采用新功能、逐个屏幕、逐个组件,有些东西会逐渐转向并发模式。所以,如果你想现在就开始准备,你需要注意一些固定的前期成本。有些事情你可以逐步解决。所以,其中一个固定的问题,如果你还没有运行 Node,你可能需要先解决这个问题,这样到今年晚些时候或者其他时候,这个问题就已经解决了。很多人如果使用 Relay 或者兼容严格模式,就已经解决了。
      • Andrew:我们现在有一个 API,你可以开始让你的组件兼容严格模式。我们几年前发布了一个名为“严格模式”的 AI,旨在在开发过程中发现某些并发问题,以便你在并发模式发布之前就能解决这些问题。它的基本功能是,它会对一些在并发模式下无法正常工作的旧类组件生命周期发出警告。它还有一个非常重要的功能,那就是它会在开发过程中重复调用纯渲染函数,以尝试消除任何可能的副作用。
      • Andrew:我们有一整份文档,描述了如何将严格模式封装到应用程序的某些部分,以便逐步完成迁移。这种从小处着手,然后逐步扩展直至覆盖更多范围的总体策略,在 React 18 之后的版本中也大致如此。需要强调的是,我认为我们过去在沟通并发模式兼容性时可能有些过于迂腐了。
      • Andrew:我们意识到,将 Facebook 界面转换为并发模式后,很多理论上的问题在实践中并不常见。我的意思是,当这些问题真的出现时,确实很烦人,但我们已经能够转换大量应用程序,而问题并不多。所以,即使并发模式最终被淘汰,我们也会为您提供解决方案。例如,如果您有一些生命周期不安全的旧类组件在代码树中未使用并发功能的部分运行,我们也没有理由向您发出警告。因此,我们会提供一种方法,您可以选择不显示这些警告,等到您真正开始采用这些功能后再显示,或者使用严格模式组件 API 提前修复这些问题。但总的来说,我们正在努力确保它能够逐步适应,并且只有在您开始在应用程序的某个特定部分使用新功能时,才需要支付迁移成本。
      • 是的,简短回答一下:
        • 如果您想从今天开始,您可以开始使用严格模式来解决这些问题,并且希望在开始逐步添加功能的那一天到来时,您已经做好了充分的准备。
        • 我要提到的另一件事是,我之前说过,在实践中你很少遇到并发模式的 bug,这种情况在组件和 Hook 中很常见。但在框架或底层代码中可能就没那么常见了。所以会有一些工作要做,这就是为什么我们计划在发布最终版本之前先发布一个候选版本,因为我们会与开源库的作者合作,特别是那些进行大量状态管理或从外部数据源读取数据的库。这些库往往存在最多的并发问题,所以对我们来说,确保生态系统畅通无阻非常重要……等到我们正式发布时,大家就可以畅通无阻地迁移了。但好处是,即使听起来有点吓人,如果我们修复了,比如说,我只会选择 Redux,如果我们修复了 Redux 的并发模式,那么我们就能为所有人修复它。我们在 Facebook 的 Relay 上已经这样做了,修复了 Relay 中许多并发兼容性问题,之后 Facebook 所有使用 Relay 的东西,包括大量的代码,基本上都恢复正常了。希望以上内容能给大家一些启发。
        • Seb:另一部分是关于你现在如何进行数据获取。如果你将数据获取交错到规范化的存储中,并以这种方式混合和匹配状态和数据,那么可能很难知道如何区分客户端部分和服务器部分。或者,你可能希望保留应用程序的某个部分,但对于你真正认为服务器组件可能有用的部分,能够将数据获取部分与状态部分分离是很好的。Next.js 中的 getInitialProps 或 getServerProps 模式对此特别有用,因为很明显,这些就是初始渲染过程所需的所有数据,否则你甚至可能使其失效。至于你需要做的任何其他使数据更加动态化的事情,那就是另一回事了。因此,无论您是否使用 Next.js,该模式都是一个很好的准备方法,因为一旦采用它们,您就可以将所有 getInitialProps 或 getServerProps 数据提取放入服务器组件中。
        • Lauren:除了 Andrew 和 Sebastian 强调的要点之外,我还想补充一点,当服务器组件开源后,我认为我们也会开源一些我们编写的内部 lint 规则以及转换脚本,这些脚本应该可以帮助你将部分组件转换为服务器组件或共享组件。具体到转换脚本,它实际上不会改变你的应用程序架构或组件树的结构,但它会识别哪些组件可以是服务器安全组件或共享安全组件,如果是,它会尝试转换这些组件,并执行一系列其他操作,以确保重命名的文件被正确导入等等。
  • Dan:从 Andrew 提到的所有要点,尤其是 Sebastian 提到的,如果能够将客户端状态与数据需求分离,这将极大地帮助转换脚本理解哪些组件实际上是服务器安全组件或共享安全组件,然后它就能帮你完成转换。我们会尽量将这些组件与服务器组件一起发布。时间安排可能不一定一致,但我一定会尽力做到。
文章来源:https://dev.to/swyx/an-annotated-guide-to-the-react-server-components-demo-2a83
PREV
每个 Web 性能测试工具
NEXT
100 字节的 CSS 让网站在任何地方都看起来很棒 100 字节的 CSS 让网站在任何地方都看起来很棒