RSC与流式渲染,为什么要用和怎么用

一、CSR,SSR,RSC的对比

传统的SSR需要服务器拿到所有数据后返回html文件,而流式渲染下服务器返回的是一个html流,服务器可以快速地将内容呈现给用户,而不是白屏干等。

下面这张图可以更直观感受两者区别:

文章参考 bobi.ink/2023/06/05/...

二、使用RSC的注意事项

  1. 因为传统的SSR心智负担就比CSR应用大上很多,因为你要考虑这段代码是会跑在服务器端还是浏览器端还是两端都会执行。而流式渲染SSR在心智负担上比传统SSR更大,因为server component 是不允许增加交互逻辑的。参考 nextjs.org/docs/app/bu...
  2. client componet和server componet都会在服务端渲染
  3. client componet一定要保证服务端和客户端渲染的内容一致的,所以尽量不要使用isInBrowser此类的api去区分渲染。
  4. 如果有些只想在客户端引入,那么使用dynamic动态引入一个组件,但是这种引入会增加一个chunkjs的加载。自行斟酌
  5. npm run build的时候会执行页面的代码,如果页面中使用了服务端才有的依赖或者req,编译态都没有这些,取值的时候需要做写法兼容

三、使用RSC的场景

适合的业务场景

  1. 数据密集型应用:RSC允许在服务器上渲染组件,这可以减少客户端的 JavaScript 大小和执行时间,对于数据密集型应用(如后台表单页、表格列表页等)非常有用。
  2. 需要快速首屏渲染的应用:RSC和流式渲染的目标是提高首屏渲染的速度,而不是降低它。这是通过在服务器端进行更多的工作(如数据获取和组件渲染),并尽早将结果发送到客户端来实现的。 在传统的客户端渲染模型中,浏览器需要下载、解析和执行 JavaScript,然后 JavaScript 会获取数据并渲染组件。这个过程可能会导致首屏渲染变慢,特别是在网络或设备性能较差的情况下。 使用 RSC 和流式渲染,服务器可以在发送 HTML 到浏览器之前获取数据和渲染组件。这意味着浏览器可以更早地开始显示内容,而不需要等待所有的 JavaScript 和数据都准备好。 然而,如果服务器端的数据获取或渲染时间过长,那么这可能会影响首屏渲染的速度。为了解决这个问题,你可以使用各种优化技术,如数据预加载、缓存、代码拆分等。 总的来说,RSC 和流式渲染是否会导致首屏渲染变慢,取决于许多因素,包括服务器的性能、网络条件、数据获取的速度、应用的复杂性等。在大多数情况下,它们都可以提高首屏渲染的速度。

不适合的业务场景

  1. 需要大量客户端交互的应用:如果需要大量的客户端交互,那么使用 RSC 可能不是最佳选择,因为 RSC 主要用于渲染不需要客户端交互的组件。
  2. 需要完全控制渲染过程的应用:如果需要完全控制渲染过程,那么流式渲染可能不适合,因为流式渲染会在数据到达时立即发送 HTML 到浏览器,这可能会导致失去对渲染过程的一些控制。

四、如何next里使用RSC

由于RSC是服务端渲染的,不会在客户端运行,因此他们不能直接处理客户端时间,如onClick。

如果需要处理客户端事件,那么可以采用RSC+客户端组件的混合模式。

首先,开启next的App Router模式:Upgrading: App Router Migration | Next.js (nextjs.org)

RSC

typescript 复制代码
function sleep(time: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}
async function getContent() {
  await sleep(1000);
  return { configs: 'ServerComponent' };
}

export default async function ServerComponent() {
  const content = await getContent();

  return (
    <div>
      {JSON.stringify(content.configs)}
      <br />
    </div>
  );
}

RSC+SSR客户端组件

rsc负责数据,客户端组件负责交互

typescript 复制代码
//RSC

import ClientComponentInServerComponent from './ClientComponentInServerComponent';

function sleep(time: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, time);
  });
}
async function getContent() {
  await sleep(1000);
  return {
    configs: 'ServerComponent',
    data: [
      { id: 1, name: 'data1' },
      { id: 2, name: 'data2' },
      { id: 3, name: 'data3' },
      { id: 4, name: 'data4' },
    ],
  };
}

export default async function ServerComponent() {
  const content = await getContent();

  return (
    <div>
      {JSON.stringify(content.configs)}
      <ClientComponentInServerComponent data={content.data} />
      <br />
    </div>
  );
}

typescript 复制代码
// 客户端组件
'use client';

interface DataItemType {
  id: number;
  name: string;
}

function ClientComponentInServerComponent({ data }: { data: DataItemType[] }) {
  console.log(data, 'data');
  const handleClick = (item: DataItemType) => {
    console.log('Clicked item:', item);
  };

  return (
    <ul>
      {data.map((item) => (
        <li key={item.id} onClick={() => handleClick?.(item)}>
          <div> {item.name}</div>
        </li>
      ))}
    </ul>
  );
}

export default ClientComponentInServerComponent;
相关推荐
用户63879947730543 分钟前
每组件(Per-Component)与集中式(Centralized)i18n
前端·javascript
SsunmdayKT43 分钟前
React + Ts eslint配置
前端
开始学java1 小时前
useEffect 空依赖 + 定时器 = 闭包陷阱?count 永远停在 1 的坑我踩透了
前端
zerosrat1 小时前
从零实现 React Native(2): 跨平台支持
前端·react native
狗哥哥1 小时前
🔥 Vue 3 项目深度优化之旅:从 787KB 到极致性能
前端·vue.js
青莲8431 小时前
RecyclerView 完全指南
android·前端·面试
青莲8431 小时前
Android WebView 混合开发完整指南
android·前端·面试
GIS之路1 小时前
GDAL 实现矢量数据转换处理(全)
前端
大厂技术总监下海1 小时前
Rust的“一发逆转弹”:Dioxus 如何用一套代码横扫 Web、桌面、移动与后端?
前端·rust·开源
加洛斯1 小时前
SpringSecurity入门篇(2):替换登录页与config配置
前端·后端