React Server Components 实战场景分析

avatar
FE @字节跳动

关于 React Server Components(下文统称为RSC) 的概念本文不做赘述,React官方有相关说明,感兴趣的同学可以参考以下地址:

本篇文档主要分析我们可以围绕RSC做哪些事情,以及如何利用RSC来优化项目性能等。当然,就目前阶段来说,官方并没有发布正式的版本,技术细节随时可能会发生变化,因此并不建议将此投入到生产环境中。

运行原理

运行过程

  • 服务启动
  • 运行时

服务启动

image.png 从上图可以看出,命令 start:dev 并行执行了两个命令:

  • npm run server:dev:执行api.server.js,启动express服务器。
  • npm run bundler:dev:打包客户端代码。

打包这里需要额外注意下,与之前的打包不同的是,除了正常打包客户端代码外,还用到了ReactServerWebpackPlugin这个插件,它的作用是遍历项目中的所有的xxx.client.js|jsx客户端组件(React把形如xxx.client.js|jsx的文件约定为客户端组件,xxx.server.js|jsx约定为服务端组件,不带后缀的约定为共享组件),打包出对应的chunk文件,这些文件的作用后面会有介绍。

打包产物如下:

image.png

运行时

image.png

加载流程

接口返回的JSON数据本质上就是一个普通的字符串,通过了换行符\n来格式化。每一行都是独立的数据单元,他们遵循共同的数据结构是:

`${数据类型}${数据单元ID}:${可解析的json序列}`

这里主要部分是数据类型,针对不同的数据类型执行不同的渲染流程

M - client组件的chunk引用信息,server组件引用了client组件会返回,就是上文提到的额外打包的clientx.main.js
J - server组件在服务端渲染的结果。会被react-server-dom-webpack解析为react element然后渲染
S - Suspense
E - 服务端渲染过程发生的错误信息,会被react-server-dom-webpack解析出来,并使用组件的FallbackComponent(<Error />组件)显示出来。
更新流程

对于服务端而言,每次处理请求的过程是相同的,区别主要在客户端的处理过程:

  • 在浏览器解析类json数据的时候,如果某个client组件的bundle包已经加载过,那么浏览器就不会再次发出该bundle包的请求了
  • 界面更新阶段,存在一个叫reconciles(协调)的行为,目的是尽量减少DOM操作,同时还能保存页面的UI状态,比如输入框的focus状态等。

优缺点分析

在使用RSC之前,我们首先要了解一下它目前对比其他渲染方式如CSR,SSR等的优势,以及它目前存在的问题。

优势

  • 性能提升
    • 解决请求瀑布流的问题,提升渲染效率
  • 优化打包体积,提升打包与加载速度
  • 自动代码分割,按需加载
  • 体验提升
    • 解决SSR状态丢失的问题
  • 默认流式渲染,可以结合Suspense较少白屏等待时间
  • 访问服务器资源
    • 访问数据库
  • 访问内部服务
  • 访问文件系统

缺点

  • 开发成本
    • 组件需要放在客户端+服务端共同管理,耦合度高,维护成本高
  • 调试较为繁琐
  • 一些客户端能力无法在此场景下发挥,比如组件状态,事件监听器等
  • 服务端组件引用客户端组件时,props无法传递函数参数
  • 由于RSC是无状态的,因此在状态管理上需要增加额外的心智负担。
  • 用户体验
    • 无法对SEO进行优化,因此使用场景具有局限性

结论分析

基于以上对RSC的优劣势的分析,可以发现RSC对于用户体验上的优化是很明显的,但在一定程度上对于开发人员的开发成本和维护成本也是有所增加的。
针对以上结论发现,除了一些对性能要求极高的场景外,业务层面RSC是不建议大规模去使用,那么我们对于这样的一个技术,应该怎么使用来保证收益最大化呢?

场景示例

以下示例是把服务端组件进行了独立部署,以探索独立提供服务端组件的可能性
官方示例参考:github.com/reactjs/ser…

时间格式处理

  1. 服务端暴露接口 /formatDate

  1. 编写服务端组件

  1. 客户端使用

  1. 页面渲染结果

服务端数据请求展示

这里客户端细节不做赘述

  1. 服务端暴露接口 /filminfo
  2. 服务端组件

  1. 页面渲染结果

图片加logo水印

服务端对传入的图片进行下载并添加logo,然后进行渲染返回

页面渲染结果

image.png

应用场景分析

通用组件库

工程中使用的UI组件类可以抽离出部分展示类组件使用RSC实现,提供接口独立对外开放

工具类组件

利用RSC可以在服务端引入依赖包的特性,我们可以把工具类的组件使用RSC实现,减少打包体积。 比如时间格式处理类组件,官方示例中的将MD解析为HTML字符串的组件等等

数据展示类组件

列表/详情展示类组件,把数据请求放在RSC中进行,减少客户端TCP建连

组件市场

用户可以在市场上使用现有的组件或者自定义上传组件,类似于npm托管模式,形成RSC市场生态