《前端暴走团》,喜欢请抱走~大家好,我是团长林语冰。
RSC(React 服务器组件)增强了 React 的基建,超越了纯粹渲染库的范畴,将数据请求和远程客户端-服务器通信纳入框架内。
在本文中,我们会深度学习:
- 为何要提出 RSC 范式?
- 何处是 RSC 的用武之地?
- 何时是使用 RSC 的最佳时机?
免责声明
本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Understanding React Server Components。
为何我们需要 RSC?
这就需要回顾一下"前 React 时代"的开发缺陷了。
使用诸如 PHP 之类的编程语言,我们在客户端和服务器之间牵桥搭线。在整体架构中,我们可以访问服务器,直接在正在创建的页面中调用数据。
虽然但是,这也美中不足,即由于跨团队依赖和高流量需求,难以扩展单体应用程序。
为了可组合性和增量采用现有代码库,React 应运而生。为了响应丰富交互性的需求,React 分离了客户端和服务器的关注点,使前端的组合更加灵活。
这对于团队开发而言至关重要:两个 React 组件,每个组件可以由不同的开发者独立制作,也可以精诚合作,因为所有组件在同一个 React 框架中运行。
为了实现这个目标,React 必须在现有的 Web 标准之上破而后立。
经过过往十年 MPA(多页面应用程序)和 SPA(单页面应用程序)、CSR(客户端渲染)和 SSR(服务端渲染)之间的演变,目标始终如一:
- 快速数据服务
- 提供丰富的交互性
- 维护优秀的开发体验
SSR + React Suspense 的用武之地
为了深度学习 RSC 的需求,首先要了解 SSR 和 Suspense 的需求。
SSR 重点关注初始页面加载,将预渲染的 HTML 发送到客户端,然后必须使用下载的 JS 进行水合(hydrate),再然后才能像经典的 React 应用程序一样运行。SSR 有且仅有发生一次:当且仅当直接导航到页面时。
只使用 SSR 时,用户可以快速获取 HTML,但必须等待"全有或全无"的瀑布流(waterfall)才能与 JS 交互,具体而言:
- 在展示任何数据之前,必须从服务器获取所有数据。
- 所有 JS 必须先从服务器下载,然后客户端才能使用。
- 所有水合作用都必须在客户端完成,然后才能交互。
为了搞定这个问题,React 创造了 <Suspense>
组件,它允许服务器端 HTML 流和客户端上的选择性水合。通过使用 <Suspense>
封装组件,我们可以告诉服务器取消该组件的渲染和水合优先级,让其他组件加载,而不会被较臃肿的组件阻塞。
当我们在 <Suspense>
中拥有多个组件时,React 会按照编写顺序沿着树向下工作,允许我们在应用程序中以最佳方式流式传输。
虽然但是,如果用户尝试与某个组件交互,那么该组件会优先于其他组件。
这极大地改善了现状,但仍存在某些遗留问题:
- 在展示任意组件之前,必须从服务器请求整个页面的数据。搞定这个问题的不二法门是,在
useEffect()
钩子中请求客户端数据,该钩子的往返时间比服务端请求的时间更长,且当且仅当在组件渲染和水化之后发生。 - 所有页面的 JS 最终都会被下载,即使它是异步流式传输到浏览器。随着应用程序复杂性的增加,用户下载的代码量也会增加。
- 尽管优化了水合作用,但在下载并为该组件实现客户端 JS 之前,用户仍然无法与组件交互。
- 大多数 JS 计算量仍然在客户端上,客户端可以在任意类型的设备上运行。所以,为什么不干脆将其转移到更强大、可预测的服务器上呢?
没有 RSC 的现状:
RSC 的用武之地
为了搞定上述问题,React 创造了 RSC。RSC 单独请求数据,且完全在服务端渲染,生成的 HTML 会流入客户端 React 组件树,并按需与其他服务器和客户端组件交互。
此过程消除了客户端重新渲染的需要,提高了性能。对于任何客户端组件,水合可以与 RSC 流入同时发生,因为计算负载在客户端和服务器之间共享。
换而言之,服务器功能更加强大,且物理上更接近数据源,它处理计算密集型渲染,且只向客户端发送交互式代码片段。
当 RSC 由于状态变更而需要重新渲染时,它会在服务器上刷新,且无缝合并到现有 DOM 中,而无需硬刷新。因此,即使从服务器更新了部分视图,客户端状态也会被保留。
RSC:性能和打包体积
RSC 可以辅助减小客户端 JS 打包体积,并提高加载性能。
传统上,在浏览应用程序时,客户端会下载并执行所有代码和数据依赖。如果没有具有代码分割功能的 React 框架,这也意味着向用户发送它们所在页面不需要的无关代码。
虽然但是,RSC 搞定了服务器上的所有依赖关系,更接近应用程序数据的来源。RSC 只在服务端渲染代码,这比客户端计算机执行此任务要快得多。然后,React 只将这些处理结果加上客户端组件发送到浏览器。
换而言之,使用 RSC,初始页面加载更快更精简。基本客户端运行时的大小可缓存且可预测,且不会随着应用程序的增长与日俱增。添加额外的面向用户的 JS 主要是因为我们的应用程序需要通过客户端组件进行客户端交互。
RSC:<Suspense>
集成
RSC 与客户端代码完全交错,这意味着,客户端组件和服务器组件可以在同一个 React 树中渲染。通过将大部分应用程序代码移至服务器,RSC 有助于防止客户端数据请求瀑布流,快速解决服务端的数据依赖性。
在传统的 CSR 中,组件使用 React <Suspense>
来"暂停"其渲染过程并展示回退状态,同时等待异步工作完成。
使用 RSC,数据请求和渲染都发生在服务端,因此 <Suspense>
也在服务端管理等待时间,缩短了总往返时间,加快渲染回退和完成页面的速度。
值得注意的是,客户端组件在初始加载时仍然是 SSR。RSC 模型不会取代 SSR 或 <Suspense>
,而是与它们强强联手,按需向用户提供应用程序的全套服务。
使用 RSC 的现状:
RSC 的局限性
为 RSC 编写的所有代码都必须是可序列化的,这意味着,我们不能使用生命周期钩子,比如 useEffect()
或状态。
虽然但是,我们仍然可以通过服务器操作从客户端与服务器进行交互。
平衡 RSC 和客户端组件
值得注意的是,RSC 并非要取代客户端组件。优秀的应用程序利用 RSC 动态请求数据,并利用客户端组件实现丰富的交互。开发者的挑战在于明确何时使用哪种组件。
作为前端开发者,请考虑利用 RSC 进行 SSR 和数据请求,同时依靠客户端组件实现本地交互功能和用户体验。通过适当的平衡,我们可以创建高性能的应用程序。
最重要的是,我们可以在非标准环境中测试应用程序:
- 模拟较慢的计算机
- 较慢的手机
- 较慢的 WiFi
我们可能会惊讶地发现应用程序在正确的组件组合下运行得更丝滑。
RSC 并不是解决用户过多客户端 JS 负载问题的完整解决方案,但它们确实使我们有权选择何时将计算量转移到用户设备上。
高潮总结
RSC 提供了一种在组件内与服务器交互的原生方案,减轻了与动态数据交互的代码和认知负担。客户端组件像以前一样保持完整的功能和完全可用。我们的任务在于选择何时使用哪种组件。
本期话题是 ------ 你如何看待 RSC?欢迎在本文下方自由言论,文明共享。
坚持阅读,自律打卡,每天一次,进步一点。
《前端暴走团》,喜欢请抱走!我是团长林语冰。谢谢大家的点赞,掰掰~