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;
相关推荐
gnip3 小时前
企业级配置式表单组件封装
前端·javascript·vue.js
一只叫煤球的猫4 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
excel5 小时前
Three.js 材质(Material)详解 —— 区别、原理、场景与示例
前端
掘金安东尼5 小时前
抛弃自定义模态框:原生Dialog的实力
前端·javascript·github
hj5914_前端新手9 小时前
javascript基础- 函数中 this 指向、call、apply、bind
前端·javascript
薛定谔的算法9 小时前
低代码编辑器项目设计与实现:以JSON为核心的数据驱动架构
前端·react.js·前端框架
Hilaku9 小时前
都2025年了,我们还有必要为了兼容性,去写那么多polyfill吗?
前端·javascript·css
yangcode9 小时前
iOS 苹果内购 Storekit 2
前端
LuckySusu9 小时前
【js篇】JavaScript 原型修改 vs 重写:深入理解 constructor的指向问题
前端·javascript
LuckySusu9 小时前
【js篇】如何准确获取对象自身的属性?hasOwnProperty深度解析
前端·javascript