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;
相关推荐
大怪v11 小时前
AI抢饭?前端佬:我要验牌!
前端·人工智能·程序员
新酱爱学习11 小时前
字节外包一年,我的技术成长之路
前端·程序员·年终总结
小兵张健11 小时前
开源 playwright-pool 会话池来了
前端·javascript·github
IT_陈寒14 小时前
Python开发者必知的5大性能陷阱:90%的人都踩过的坑!
前端·人工智能·后端
codingWhat15 小时前
介绍一个手势识别库——AlloyFinger
前端·javascript·vue.js
代码老中医15 小时前
2026年CSS彻底疯了:这6个新特性让我删掉了三分之一JS代码
前端
不会敲代码115 小时前
Zustand:轻量级状态管理,从入门到实践
前端·typescript
踩着两条虫15 小时前
VTJ.PRO 双向代码转换原理揭秘
前端·vue.js·人工智能
扉川川15 小时前
OpenClaw 架构解析:一个生产级 AI Agent 是如何设计的
前端·人工智能
远山枫谷15 小时前
一文理清页面/组件通信与 Store 全局状态管理
前端·微信小程序