,大家好,我卡颂。,React Server Component(后文简称RSC)是React近几年最重要的特性。虽然他对React未来发展至关重要,但由于:,所以虽然体验Demo[1]已经发布3年了,但仍属于「知道的人多,用过的人少」。,本文会从以下几个角度介绍RSC:,希望读者读完本文后对RSC的应用场景有清晰的认识。,对于一个React组件,可能包含两种类型的状态:,「前端交互用的状态」放在前端很合适,但「后端请求回的数据」逻辑链路如果放在前端则比较繁琐,整个链路类似如下:,如果我们根据「状态类型」将组件分类,比如:,按照这种逻辑划分,上述代码中:,将上述代码改写为:,其中:,改造后「前端交互用的状态」逻辑链路不变,而「后端请求回的数据」逻辑链路却变短很多:,这就是RSC的理念,一句话概括就是 —— 根据状态类型,划分组件类型,RCC在前端运行,RSC在后端运行。,同样涉及到前端框架的后端运行,RSC与SSR、SSG有什么区别呢?,首先,SSG是后端「编译时方案」。使用SSG的业务,后端代码在编译时会生成HTML(通常会被上传CDN)。当前端发起请求后,后端(或CDN)始终会返回编译生成的HTML。,RSC与SSR则都是后端「运行时方案」。也就是说,他们都是前端发起请求后,后端对请求的实时响应。根据请求参数不同,可以作出不同响应。,同为后端运行时方案,RSC与SSR的区别主要体现在输出产物:,既然输出产物不同,那么他们的应用场景也是不同的。,比如,在需要考虑SEO(即需要后端直接输出HTML)时,SSR与SSG可以胜任(都是输出HTML),而RSC则不行(流式输出)。,同时,由于实现不同,同一个应用中可以同时存在SSG、SSR以及RSC。,「RSC规范」是如何区分RSC与RCC的呢?根据规范定义:,所以,我们上述例子可以导出为2个文件:,对于任意应用,按照「RSC规范」拆分组件后,能得到类似如下的组件树,其中RSC
和RCC
可能交替出现:,,但是需要注意:RCC中是不允许import RSC的。也就是说,如下写法是不支持的:,这是因为,如果一个组件是RCC,他运行的环境就是前端,那么他的子孙组件的运行环境也是前端,但RSC是需要在后端运行的。,那么上述RSC和RCC交替出现是如何实现的呢?,,答案是:通过children。,改写下ClientCpn.client.jsx:,在OuterServerCpn.server.jsx中引入ClientCpn与ServerCpn:,组件结构如下:,解释下这段代码,首先OuterServerCpn是RSC,则他运行的环境是后端。他引入的ServerCpn组件运行环境也是后端。,ClientCpn组件虽然运行环境在前端,但是等他运行时,他拿到的children props是后端已经执行完逻辑(已经获得数据)的ServerCpn组件。,我们可以将RSC看作一种rpc(Remote Procedure Call,远程过程调用)协议的实现。数据传输的两端分别是「React后端运行时」与「React前端运行时」。,,一款rpc协议最基本的组成包括三部分:,以上面的OuterServerCpn.server.jsx举例:,这段组件代码转化为RSC数据后如下(不用在意数据细节,后文会解释):,接下来我们从上述三个角度分析这段数据结构的含义。,RSC是一种「按行分隔」的数据结构(方便按行流式传输),每行的格式为:,其中:,RSC的序列化与反序列化其实就是JSON的序列化与反序列化。反序列化后的数据再根据「标记」不同做不同处理。,比如,对于上述代码中第二行数据:,可以理解为,这行数据描述了一棵组件树(标记J),id为0,组件树对应数据为:,当前端反序列化这行数据后,会根据上述JSON数据渲染组件树。,所谓「id映射」,是指 对于同一个数据,如何在rpc协议传输的两端对应上?,在「RSC协议」的语境下,是指 对于同一个组件,经由RSC在React前后端运行时之间传递,是如何对应上的。,还是考虑上面的例子,回顾下第二行RSC对应的数据:,这段数据结构有些类似JSX的返回值,把他与组件层级放到一张图里对比下:,,可以发现,这些信息已经足够前端渲染<OuterServerCpn/>、<ServerCpn/>组件了,但是<ClientCpn/>对应的数据@1是什么意思呢?,这需要结合第一行RSC的数据来分析:,M标记代表这行数据是「一个RCC的引用」,id为1,数据为:,第二行中的@1就是指「引用id为1的RCC」,根据第一行RSC提供的信息,React前端运行时知道id为1的RCC包含一个名为client1的chunk,路径为”./src/ClientCpn.client.js”。,于是React前端运行时会向这个路径发起JSONP请求,请求回<ClientCpn/>组件对应代码:,如果应用包裹了<Suspense/>,那么请求过程中会显示fallback效果。,可以看到,通过协议中的:,就能将同一个RCC在React前后端运行时对应上。,那么,为什么RCC不像RSC一样直接返回数据,而是返回引用id呢?,主要是因为RCC中可能包含前端交互逻辑,而有些逻辑是不能通过「RSC协议」序列化的(底层是JSON序列化)。,比如下面的onClick props是一个函数,函数是不能通过JSON序列化的:,这里我们再梳理下「RSC协议」中「id映射」的完整过程:,RSC数据是以什么格式在前后端间传递呢?,不同于一些rpc协议会基于TCP或UDP实现,「RSC协议」直接基于「HTTP协议」实现,其Content-Type为text/x-component。,,本文从理念、原理角度讲解了RSC,过程中回答了几个问题。,Q:RSC和其他服务端渲染方案有什么区别?,A:RSC是服务端运行时的方案,采用流式传输。,Q:为什么需要区分RSC与RCC(通过文件后缀)?,A:因为RSC需要在后端获取数据后流式传输给前端,而RCC在后端编译时编译成独立文件,前端渲染时再以JSONP的形式请求该文件,Q:为什么RCC中不能import RSC?,A:因为他们的运行环境不同(前者在前端,后者在后端),由于配置繁琐,并不推荐在现有React项目中使用RSC。想体验RSC的同学,可以使用Next.js并开启App Router:,,在这种情况下,组件默认为RSC。,[1]体验Demo:https://github.com/reactjs/server-components-demo,[2]how-react-server-components-work:https://www.plasmic.app/blog/how-react-server-components-work,[3]react-server-dom-webpack:https://www.npmjs.com/package/react-server-dom-webpack,[4]Vite插件的实现 PR:https://github.com/facebook/react/pull/26926
© 版权声明
文章版权归作者所有,未经允许请勿转载。