Server Components 听起来彷佛并不那么激动民气,React 18 所发布的各种特性也彷佛平平无奇,自从 Hooks 面世已经三年多过去了,React 彷佛结束了提高的脚步,只是在现有的根本上做些小修小补?
No。
Concurrent rendering(React 18 新带来的特性)是一种实质上的改变,它本身不像 Hooks 那样对开拓体验有着近乎翻天覆地的变革,但是这种底层渲染能力/机制的调度,会带来非常非常多的可能性,例如:

Suspense、OffScreen、Server Components
这三种特性,目前都没有生产可用,但是等到未来他们正式发布并逐渐被大面积利用时,每一项特性都会带来非常显著的开拓体验的提升。而如果让我从这些未来会涌现的新特性中选一个最期待的,那绝不疑问会是 Server Component。
以是,Server Components 到底是什么?他会像当年的 Hooks 一样对全体 React 生态带来巨大的影响么?在我们回答这些问题之前,很有必要先阐明一下 Server Components 是什么,又办理了什么问题。
注:下文中的很多内容受 Dan 和 Lauren 的这份 演讲视频 [1] 所启示,如果你想更深入的理解即将到来的 React Server Component,那么非常推举这段视频 事实上,这篇文章并不是一份对 Server Components 的用法传授教化,也不会涵盖 Server Components 的每一处细节(乃至为了方便表述会故意地略过一些细节),因此, 在读下文之前,最好是对 Server Components 已经有所理解
背景:前后端分离“前后端分离”是当下主流的 web 研发模式,后端存储数据,并把对数据的操作(增编削查)封装成接口,通过后端做事供应给前端,前端运用发送要求(例如 http 要求或者 rpc 要求)去调用后端供应的接口,从而获取到数据或者是对数据进行修正。
这可能是十几年以来非常普遍的研发模式了,也因此,我们被区分成前端开拓和后端开拓,各自大责着“楚天河界”的一侧。我们在各自那一侧都做了非常多的优化、创新、打破,在后端,我们有容器化、微做事、SSR,在前端,我们有 code spliting、前端路由、React Hooks。
但是对付 API 层,我们彷佛这么多年以来都未曾有过关注,即便是有,也仅仅是勾留于 API 传输性能(例如 grpc)、API 的存在形式(例如 Restful 和 GraphQL)、API 的工程化管理(例如 Postman)。
并非是想说 API 一个邪恶而糟糕的设计,但是自从 Restful 的观点被提出以来,已经 22 年过去了,我们是不是该当在现在重新思考一下:
以网络要求作为前后真个分界是最优解吗?如果没有 API,我们该如何架构和开拓 Web 运用?症结所在让我们再回到刚刚的那张图,考虑一下 API 在带来职责分工明晰之外,同时也带来了哪些问题。
要求瀑布流(Waterfall)就像 Remix [2] 首页上所展示的,基于 API 和嵌套路由的前端站点,在要求时会涌现瀑布流的征象:
数据的之间可能是有前后的依赖关系,抑或是和组件强耦合在一起,须要等待组件的 bundle 加载完成之后才能发出要求,这些都导致了要求瀑布流征象的涌现。
并发要求后端希望实现小而美的接口,每个接口有独立的职责,例如:
getUser 获取用户信息getSongs?page=12 获取歌曲列表getNotifactions 获取关照列表getFavoirateSongs 获取收藏的歌曲getNewSongs 获取新发布的歌曲getRecommendSong 获取今日推举的歌曲及对应的文案getSearchBarHotKeywords 获取热门的搜索词getAdBanner 获取广告 banner 内容getRecentSongs 获取最近听歌记录getRecommendedPlayList 获取推举的歌单列表……(实在太多了)每一个接口,单独拿出来看都是合理的,但是放在一起,就会创造用户每次打开这样一个音乐 web app,都要发送至少十几个接口,对付一些轻微繁芜一点的网页,首次加载就须要要求几十个接口也丝毫不奇怪。
每一个接口的要求,都会带来网络开销,乃至在有些环境下会有最大并发要求数量的限定(例如在支付宝客户端那的 rpc 要求),或许网络层的 automatic batching 可以办理这个问题,但是遗憾的是,在目前的技能体系内,这个问题并不好办理(这里没有写不能办理,是由于的确有一些可行的方案,例如 BFF、依赖网关来做接口聚合,但它们都引入的新的问题)。
前端包体积(Bundle size)包体积已经是“当代”前端开拓领域饱受诟病的一点了,动辄几百 k 的 js 文件,彷佛已经背离了浏览器是用来“浏览”网页的初衷了。并不是说我们都要做一个浏览器原教旨主义者,但是如果网页能够在不丢失用户体验和开拓体验的条件下,规复到非常轻量和快速的状态,难道不是一件好事么?
协作本钱(沟通、逻辑感知和封闭)在我个人看来,这是大型项目或须要长期掩护的运用中最令人头疼的问题了。
假设我们现在有一个非常巨大的运用,须要有十几位开拓者共同编写和掩护,那如何分工?答案一定是先做模块化,我们把全体运用拆分成几个彼此只管即便独立的模块,再由每个人或每几个人卖力个中的一个模块。
模块化带来的好处是边界清晰(看到一个需求就能判断出来涉及到哪个或哪些模块做哪些改动)、职责明确(每个人都有自己确定的职责)、减少沟通本钱(由于模块内部的逻辑是封闭的,不须要外部感知,以是可以降落沟通本钱)。
对付前两点,目前的前后端分离架构都还是及格的,但对付第三点,我以为基于网络要求接口的协作模式,在很多情形下并没有有效地做到逻辑内部封闭、减少须要前后端之间来回沟通的信息量。
举个例子,对付这样的一个页面:
看起来非常大略,一些信息的展示,加上一个充值按钮,这便是我最开始所设想的。
然而,随着这个项目不断的推进,我创造,原来以为是纯静态的标题文案,实际上是须要后端掌握的,根据当前用户的所属人群来动态判断文案内容;我创造,由于前端金额打算的可靠性问题,折扣和实际支付干系的内容都是须要在后端预处理之后展示在前真个;
我创造,倒计时的参考韶光是须要依赖后端返回的;我创造,按钮的文案、点击行为,是须要后端掌握的,特殊是按钮的点击行为,终极方案是后端返回一个列举,前端根据这个值来 switch case 一下走不同的逻辑(例如下单、勾引前辈行注册和绑卡)……
为了阅读体验,我只是列举了个中随手想到的一小部分,如果总结一下,那便是,后端和前端并没有由于“前后端分离”而做到解藕,反倒是藕断丝连,剪不断理还乱。后端感知了过多的前端视图层逻辑,就像是发明了一套 DSL(Domain Specific Language),而前端则是要写一个针对这套 DSL 的解析器和渲染器。
回到我们刚刚提到的,模块化带来的好处。模块化能够降落沟通本钱,有一个不可忽略条件,便是架构的合理性。模块化并非是降落沟通本钱的实质缘故原由,也并非所有的模块化实践都能带来沟通本钱的降落。当前后端分离的实践成为一个僵硬的、去世板的“规范”,那它还能真正起到多少降落沟通本钱的浸染?一个大大的问号。
Server Components再次申明一下,下文是假设读者朋友已经对 Server Components [3] 有所理解
基于网络要求的 API 模型,有一个大大的条件假设,便是前端运用和后端运用是两个独立的运用,但是为什么一定假如这样?
或许我们可以让后端运用直接渲染 HTML,用户操作时,重新渲染一遍页面?这实在便是在 Restful 时期之前的架构,有很多弊端,特殊是可交互性差,不然也就不会涌现后来 Restful 的盛行了。
那再或许,我们可以让前真个 React 组件,运行在后端?
这便是 React Server Components。
一图胜千言,在现在的前后端分离模式下,后端供应接口,前真个 React 组件调用接口。
而如果后端可以运行 React 组件,直接渲染 React 节点树到前端,就不须要所谓的 API 的观点了。
后端运行 React 组件并不是什么新鲜事,我们在 SSR(Server Side Rending)早就习以为常了,但是须要特殊注明的一点是,在 SSR 中,后端是运行了 React 组件,天生了一份初始状态的 html,但这份 html 是没有可交互性的,它只是为了让用户能尽早看到页面而做的一种改良式的、修修补补一样的优化。
而 Server Components 所带来的,是我们可以把同一个项目中,一部分的组件作为 Server Components,另一部分组件,作为 Client Components,因此我们可以既享受到后端内部调用带来的便捷、可掩护性,又能担保页面的可交互性险些没有任何妥协。
如果你用过 PHP 或 Django,那你肯定非常熟习这种模式:后端直接渲染 html 内容,浏览器只卖力显示,用户点击按钮,那就重新要求、重新渲染页面,如果页面上须要一些繁芜的动态交互,比如让用户可以把一个列表展开/收起,或者是点击某个按钮之后展示一个模态框,那可以借助于 jQuery 来实现。
PHP + bootstrap + jQuery,现在,Server Components 就像是这套范式的升级版,可以被称为一种全新的“全栈”开拓模式。
由于是在后端环境下,这些 Server Components 可以利用全部的后端能力,不管是中间件,还是其他后端微做事的调用,乃至是 db 的访问(当然可以直接跑 SQL,但是更好的实践是通过一个数据中间层),都可以实现。这样一来,我们就可以直接把数据从源头获取,放到 React 组件的高下文中,那自然就不须要传统意义上的 API 了。
更准确的说,API 并未消逝,我们实在也不会和 API 就此说再见,而是让它换了一种形式。有模块化的地方,就会有 API,Restful 的 http 网络要求固然是 API,但中间件暴露出来的方法,浏览器供应的 Date 工具,node 供应的文件读取函数,db 供应的 SQL,这些全都是 API。
在这种新架构下,API 变成了后端里业务运用和上游做事之间的调用,变成了 Server Components 和 Client Components 之间的 props 通报,前者让 API 变得更加干净、更符合单一职责的原则,而后者让 API 变得自然到你险些感知不到。
以是:
Server Components 许可我们不再按照 前端 - 后端 进行模块的拆分,而是依照 业务运用 - 底层做事 来进行更合理的模块拆分。从而可以理论上降落模块之间的沟通本钱(由于目前还没有办法实践证明)。由于 Server Components 是在后端运行组件,直接通过网络传输给前端进行渲染,因此很多大体积的包(例如 markdown 渲染、html sanitize)都不须要在前端下载和运行,从而很大程度上降落包体积。由于底层 db 或上游做事的调用都是发生在后端内部的,因此即便涌现并发要求,所带来开销也远远小于前端并发调用后真个 Restful API。同理,要求瀑布流的问题也会由于调用开销降落而消逝或减轻。想象如果大胆想象一下的话,未来的研发模式可能这样的:
开拓者将不会再区分前端和后端,而是区分为业务运用开拓和上游做事开拓。现在的后端开拓将(真正地)不再须要关注视图逻辑,只聚焦于底层业务逻辑,为前端供应清晰好用、原子化的做事/接口;而现在的前端开拓将会拓展到横跨前端和后端(代码运行环境上),卖力的是在后端封装好的一个个原子化的底层能力上,构建视图层,而我们也须要一套全新的框架和根本举动步伐,来适配 Server Components。
目前,Server Components 还没有正式发布,而即便正式发布之后,也还有长长的工程化落地的路要走,Server Components 增加了很多额外的限定,server、client、shared 的区分也可能会带来一些理解本钱。缓存、性能、server 重新渲染时的增量更新策略、发布时的可灰度性和可回滚性、业务中边界情形的处理,还有很多的问题须要去办理,还有很多的未知尚未被验证。
参考资料[1] 演讲视频: https://www.youtube.com/watch?v=TQQPAU21ZUw
[2] Remix: https://remix.run
[3] Server Components: https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html