React RFC Server Components是什么,有啥用?

开发 前端
ServerComponent提案的出现,预示着React的长远目标:将对View层的控制细化到组件级别。

12月21日,React团队公布了一个新的提案Server Components。

伴随这个提案同时发出的,还有一个小时的视频讲解[1]、可供运行的Demo[2]、详尽的介绍。

 

可见,React团队很重视这个提案。本文会从如下方面讲解:

  • Server Components是什么
  • Server Components解决了什么问题

ServerComponent是什么

一句话概括:

Server Components是在服务端运行的React组件。

咦?这和服务端渲染(SSR)有什么区别?

相比SSR将组件在服务端渲染成填充内容的HTML字符串,并在客户端hydrate后使用。Server Components更像我们的在客户端写的普通组件一样,只不过他的运行环境是服务端。

我们可以将组件按照功能分为:

  • 提供数据的容器组件
  • 渲染数据并提供数据交互的交互组件

举个例子,Note组件是容器组件,他负责请求并缓存数据。NoteEditor是渲染note数据并执行用户交互的交互组件。

  1. function Note(props) { 
  2.   const [note, setNote] = useState(null); 
  3.   useEffect(() => { 
  4.     fetchNote(props.id).then(noteData => { 
  5.       setNote(noteData); 
  6.     }); 
  7.   }, [props.id]); 
  8.    
  9.   if (note == null) { 
  10.     return "Loading"
  11.   } else { 
  12.     return <NoteEditor note={note}/> 
  13.   } 

如例子所述,我们可以通过在useEffect中发起请求并将返回的数据保存在state中。

这种「请求-渲染」模式会遇见被称为waterfall的问题:

就像一节一节的瀑布往下流水,NoteEditor需要等待Note请求note成功后才能开始渲染。

[[359453]]

当交互组件依赖的数据源越多,waterfall问题会更明显。

理论上,如果React足够聪明,就能在服务端执行容器组件的渲染逻辑,在客户端执行交互组件的渲染逻辑。

按照这样的理念,如下这棵完全在客户端渲染的组件树:

 

可以拆分为:在服务端运行的容器组件和在客户端运行的交互组件。


其中在服务端运行的容器组件就是Server Component。

ServerComponent的意义

既然ServerComponent在服务端运行,天然更接近各种IO(请求数据库、读取文件、缓存...)。

上面的例子完全可以直接从数据库获取note数据,同时借助Suspense,采用同步的写法。

  1. function Note(props) { 
  2.   const note = db.notes.get(props.id); 
  3.   if (note == null) { 
  4.     return "Loading"
  5.   } 
  6.   return <NoteEditor note={note}/> 

天然更接近后端

任何其他数据源只需要通过React提供的API简单封装,使其支持Suspense,就能接入ServerComponent中。天然更接近后端。

解决waterfall

区别于SSR传输的HTML字符串。ServerComponent会将Note组件及其从IO请求到的数据序列化为类似JSX的数据结构,以流的形式传递给前端:


客户端在运行时直接获取到填充了数据的流,并借助Concurrent Mode执行流式渲染。

0打包体积

假设我们开发一款MD编辑器。服务端传递给前端MD格式的字符串。

我们需要在前端引入将MD解析为HTML字符串的库。这个库就有206k。

  1. import marked from 'marked'; // 35.9K (11.2K gzipped) 
  2. import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped) 
  3.  
  4. function NoteWithMarkdown({text}) { 
  5.   const html = sanitizeHtml(marked(text)); 
  6.   return (/* render */); 

通过ServerComponent我们怎么解决这个问题呢?

只需要简单将NoteWithMarkdown标记为ServerComponent,将引入并解析MD这部分逻辑放在服务端执行。

ServerComponent并不会增加前端项目打包体积。这个例子中,一次性为我们减少了前端206K (63.3K gzipped)的打包体积以及解析MD的时间。

自动代码分割

通过使用React.lazy可以实现组件的动态import。之前,这需要我们在切换组件/路由时手动执行。在ServerComponent中,都是自动完成的。

图片

在上面动图中,左侧列表是ServerComponent,当点击其中卡片时,组件对应数据会动态加载。

更好的ahead-of-time (AOT)优化

Vue作为一门使用模版语言的框架,模版语言的固定写法使其能在编译时针对模版内容作出优化。

由于JSX仅仅是JS的语法糖,React很难在编译时做出优化。

ServerComponent对组件提出了更多限制(不能使用useState、useEffect...)。这些限制从侧面为AOT提供更多优化线索。

ServerComponent的使用

下面我们通过改写一个记事本组件讲解ServerComponent的使用:

  1. // Note.js  
  2.  
  3. import fetchData from './fetchData';  
  4. import NoteEditor from './NoteEditor'
  5.  
  6. function Note(props) { 
  7.   const {id, isEditing} = props; 
  8.   const note = fetchData(id); 
  9.    
  10.   return ( 
  11.     <div> 
  12.       <h1>{note.title}</h1> 
  13.       <section>{note.body}</section
  14.       {isEditing  
  15.         ? <NoteEditor note={note} /> 
  16.         : null 
  17.       } 
  18.     </div> 
  19.   ); 

 Note组件的主要功能是根据props传入的id请求对应的note数据。

NoteEditor用于展示及修改note。

其中fetchData方法用于获取数据,数据的加载中状态由组件外的Suspense完成。

可以看到,交互部分由NoteEditor完成,Note主要功能是获取并传递数据。

接下来我们将Note变为ServerComponent。

  1. // 注意🙋 
  2. // Note.server.js - Server Component 
  3.  
  4. // 注意🙋 
  5. import db from 'db.server';  
  6. // 注意🙋 
  7. import NoteEditor from './NoteEditor.client'
  8.  
  9. function Note(props) { 
  10.   const {id, isEditing} = props; 
  11.   const note = db.posts.get(id); 
  12.    
  13.   return ( 
  14.     <div> 
  15.       <h1>{note.title}</h1> 
  16.       <section>{note.body}</section
  17.       {isEditing  
  18.         ? <NoteEditor note={note} /> 
  19.         : null 
  20.       } 
  21.     </div> 
  22.   ); 

  有3点需要注意的改动,我们依次了解下:

  1. Note.js文件名改为Note.server.js代表这是Server Component。
  2. Note.server.js运行于服务端,我们不需要客户端的fetchData方法,可以直接访问数据库,所以这里调用db.server提供的方法
  3. NoteEditor用于展示及修改note。这是由客户端用户的交互控制的,所以将文件名改为NoteEditor.client代表这是个Client Component。

总结

太阳底下没有新鲜事。早期前端交互简单,仅仅作为服务端的View层。

随着前端交互变复杂,出现了前端框架主导的客户端渲染(CSR)。

为了解决首屏渲染速度、SEO问题,出现了服务端渲染(SSR),又回到了曾经作为View层的起点,只不过控制的粒度更细。

ServerComponent提案的出现,预示着React的长远目标:将对View层的控制细化到组件级别。

为什么是「长远目标」?ServerComponent落地的大前提是Concurrent Mode生产环境稳定,让我们一起期待2021年吧。

参考资料
[1]视频讲解:https://www.youtube.com/watch?v=TQQPAU21ZUw

[2]Demo:https://github.com/pomber/server-components-demo/

 

责任编辑:姜华 来源: 魔术师卡颂
相关推荐

2022-05-09 08:22:09

ReactHooks

2022-01-17 10:07:05

PodmanDocker容器

2020-08-10 07:44:13

虚拟内存交换内存Linux

2021-09-05 18:28:10

数据分析模型

2021-05-28 22:40:01

稳定币数字货币货币

2023-12-20 14:48:26

2019-10-25 09:50:03

网络爬虫蜘蛛Wi-Fi

2024-10-18 10:00:00

云计算虚拟

2021-07-13 09:08:19

磁盘阵列系统

2021-01-07 14:20:55

JavaGC

2024-11-12 13:34:25

2021-12-28 20:05:19

数字交通信息

2022-09-23 10:25:00

VueReact

2020-10-20 09:57:04

量子计算人工智能技术

2021-07-02 07:06:20

React组件方式

2021-08-23 06:22:00

PaaSDevOps平台即服务

2021-01-21 17:27:05

区块链加密货币稳定币

2023-03-24 12:34:56

2021-10-09 13:08:58

C++STLSet

2022-10-20 23:21:21

点赞
收藏

51CTO技术栈公众号