前段时间 React 团队公布了一项用于解决 React 页面在多接口申请下的性能问题的解决方案 React Server Components。当然该计划目前还在草案阶段,官网也只是发了视频和一个示例 demo 来阐明这个草案。
Server Components
官网在视频和 RFC 中阐明了产生这个计划的次要起因是因为大量的 React 组件依赖数据申请能力做渲染。如果每个组件本人去申请数据的话会呈现子组件要等父组件数据申请实现渲染子组件的时候才会开始去申请子组件的数据,也就是官网所谓的 WaterFall 数据申请队列的问题。而将数据申请放在一起申请又十分不便于保护。
既然组件须要数据能力渲染,那为什么接口不间接返回渲染后的组件呢?所以他们提出了 Server Components 的解决方案。咱们暂且不论这其中的逻辑有没有情理,先来看看该计划的大体流程是怎么的。
计划的大略就是将 React 组件拆分成 Server 组件(.server.tsx
)和 Client 组件(.client.tsx
)两种类型。其中 Server 组件会在服务端间接渲染并返回。与 SSR 的区别是 Server Components 返回的是序列化的组件数据,而不是最终的 HTML。
可能带来的问题
通过接口将组件和组件的数据一并返回的形式带来了打包体积的劣势,然而它真的能像 React Hooks 一样香吗?我感觉并不然。
接口返回
惯例做法里前端 JS 中加载组件,接口返回组件须要的数据。而 React Server Components 中则是将二者合二为一,尽管在打包体积上有所优化,然而显著是把这体积本义到了接口返回中。特地是在相似列表这种有分页的申请中,这种劣势会更显著。明明组件只须要在初始的时候进行加载,然而因为被交融进接口里了,每次接口都会返回冗余的组件构造,这样也不晓得是好还是不好。可能后续须要优化一下接口二次返回只返回数据会比拟好。
服务器老本问题
这里所说的服务器老本有很多,首先是机器自身的老本。将客户端渲染行为迁徙到服务端时候势必会减少服务端的压力,用户量上来之后这块的老本是成量级的在减少的。对于这个问题,官网提供的回复是随着服务器的老本升高势必 Server Components 带来的劣势会对消这块的劣势。
Question: This might become more expensive for applications. In the search demo, finding those search results plus rendering them on the server is a more expensive operation than just an API call sent from the client.
Reply: We are moving some of the rendering to the server–so it’s true that your server will be doing more work than before. But server costs are constantly going down, and far more powerful than most consumer devices. I think React Server Components will give you the ability to make that tradeoff and choose where you best want the work to be done, on a per component basis. And that’s not something that’s easily possible today.
via:《RFC: React Server Components》
不过以目前我所在的业务状况来看,服务器的老本还是十分贵的,为了降低成本大家纷纷将逻辑下发到边缘计算甚至是客户端解决。一方面是为了节省成本,另一方面也是为了升高压力放慢解决。
除了机器自身的老本之外,申请的老本也会减少。毕竟除了数据申请之外还要解决组件渲染,而且这块作为组件耦合不好进行拆分。相比拟惯例计划,应用 JS 文件加载组件到客户端,接口单纯返回数据,这块的工夫成本增加了十分多。特地是惯例计划中 JS 文件加载完之后是在浏览器中缓存的,后续的老本十分小。
体积问题可能还好,然而申请工夫减少了这个可能就十分致命了。
心智累赘
这点在 RFC 中也有阐明。因为 Server Components 中无奈应用 useState
, useReduce
, useEffect
, DOM API 等办法,势必这会给使用者带来大量的心智累赘。尽管官网说会应用工具让开发者做到无感,且会提供运行时报错,然而我置信光是想什么时候须要写 Server Componet 什么时候须要写 Client Component 就曾经脑壳疼了吧,更别提还有个 Shared Component 了。
另外还有就是减少了跨端的流程之后,调试的老本也会变的十分高。别说很多人没有服务端的教训,就算是有相干教训的同学可能也没方法很好的在服务端进行疾速定位。对于这个问题官网提供的说法是能够依赖外部的谬误监控和日志服务。
回归问题的实质
让咱们回归到问题的实质,React Server Component 的目标其实是为了解决接口申请扩散在各组件中带来的子组件的数据申请须要期待父组件申请实现渲染子组件时能力开始申请的数据申请队列问题。那么除了 Server Component 之外没有其它的解决方案了吗?其实不然。
import React, {useState, useEffect} from 'react';
import ReactDOM from 'react-dom';
function App() {const [data, setData] = useState([]);
useEffect(() => {fetchData.then(setData);
}, []);
return (
<div>
{!data.length ? 'loading' : null}
<Child data={data} />
</div>
);
}
function Child({data}) {const [childData, setData] = useState([]);
useEffect(() => {fetchChildData.then(setData);
}, []);
if(!data.length) {return null;}
return (<div>{data.length + childData.length}</div>
);
}
ReactDOM.render(<App />, document.querySelector('#root'));
如示例代码所示,只有加载组件,然而在无数据状况下不返回 DOM 也是能够做到子组件的数据先申请而无需期待的。当然这种须要认为的在写法上进行优化,但我也依然认为比大费周章的去做 Server Component 要好很多。
至于 Server Component 带来的打包体积优化这个问题,我感觉 RFC 外面的评论说的十分的好。”比起 83KB(gzip 后大略是 20KB)打包体积,我感觉在我的项目中为了格式化日期应用一个 83KB 的库这才是更大的问题。“
Removing a 83KB (20KB gzip) library isn’t a big deal, I would say the bigger problem here is that you’re using a 83KB library to format dates.
via:《RFC: React Server Component》
实际上官网列举的两点对于日期解决以及 Markdown 格局解决的库,能够看到都是针对于数据进行解决的需要。针对这种状况如果感觉这块的体积十分”贵“的话齐全是能够让服务端将格式化后的数据返回,这样岂不是更小老本的解决了这个问题?
后记
看完《RFC: React Server Component》中所有的探讨,大部分人对 Server Component 还是持不赞成的态度的,认为它可能并没有像 React Hooks 那样解决业务中的理论痛点。就目前裸露的提案,我集体也感觉 Server Component 是弊大于利的。目前就冀望官网如果要实现的话能解耦实现,不要影响未应用 Server Component 的 React 用户打包体积。
当然该提案我感觉不是没有益处,它最大的益处我集体认为是带来了 React 组件序列化的官网规范。为多端、多机、多语言之间实现 React 组件交换提供了根底。基于这套序列化计划,咱们能够实现组件缓存存储,多机器并发渲染组件等。至于多语言实现也是在 RFC 探讨中大家比较关心的问题,通过这套序列化规范让其它语言去实现 React 组件也不是没有可能。