RSC,如何拆分的客户端与服务端组件

2025-9-15 399 9/15

                                                                                                             区别

SSR都应该会比较熟悉,就是服务器在每次请求时动态生成完整的 HTML 页面并发送给客户端。

CSR则是完全在客户端渲染,也就是我们常用的单页应用,服务器只返回一个html壳,里面包含一个div根元素,然后加载js,执行render函数,渲染。

RSC感觉是两者的结合,react18推出的,会将组件分为客户端组件与服务端组件(这里的客户端组件并不是指他的初次渲染会在客户端渲染,他也会在服务端进行一次预渲染)。

 

                                                                                              RSC的客户端与服务端组件

在next中,默认的情况下 在page与layout中都是默认的服务端组件

服务端组件:可以直接访问node APi,也就意味着可以直接访问数据库,有后端能力,但是他不能访问BOM与DOM,也不能使用hooks,这也意味着他仅仅只能是纯静态的组件。

客户端组件需要在文件的顶部编写 'use client',客户端组件并不是指组件会与单页应用一样,初次渲染在客户端。它也会在服务端进行一次渲染,所以不能直接在组件与函数中访问到DOM与BOM(预渲染不会执行任何副作用函数,state的值会使用初始的state的值),这里对比一下代码,与服务端返回的html与我们最终页面的呈现。

看一下代码rootLayout与

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {

  return (
    <html lang="zh-CN">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >  
        <main className="w-full min-h-screen bg-[var(--background)] overflow-x-hidden">
           <NavBar />
          {children}
        </main>
      </body>
    </html>
  );
}

page代码

"use client";

import { useState, useEffect } from "react";
import { Pagination, Input, } from 'antd';
import { Card } from "@/app/components/article";

const { Search } = Input;

export default function Home() {
  const [articles, setArticle] = useState(Array.from({length: 10}, (_,index) =>
    ({id: index, title: `文章${index}`, content: `文章内容${index}`})))
  const [categories, setCategories] = useState(Array.from({length: 10}, (_,index) =>
    ({id: index, name: `分类${index}`})))
  
  const fetchAll = async () => {
    // const res = await getArticles()
    // setArticle(res.data)
    setArticle(Array.from({length: 10}, (_,index) =>
      ({id: index, title: `文章test${index}`, content: `文章内容test${index}`})))
  }

  useEffect(() => {
    fetchAll()
  }, [])

  return (
    <div className="flex gap-12 p-32 pt-18 bg-[var(--background)] max-w-full overflow-x-hidden">
      <div className="flex-1 flex flex-col gap-8">
       { articles.map((item,index) => <Card key={index} {...item}/>)}
       <Pagination />
      </div>
      <div className="flex w-80 flex-col gap-8">
        <div 
          className="rounded-2xl p-6 border backdrop-blur-md 
            bg-[var(--background-secondary)] border-[var(--border-primary)]"
          style={{ boxShadow: 'var(--shadow-md)' }}
        >
        <Search placeholder="文章搜索" size="large" />
        </div>
        <ul
          className="rounded-2xl px-6 py-4 flex flex-col gap-2 border backdrop-blur-md
            bg-[var(--background-secondary)] border-[var(--border-primary)]"
          style={{ boxShadow: 'var(--shadow-md)' }}
        >
         { categories.map((item,index) => <li key={index} className="h-8 flex ">{item.name} </li>)}
        </ul>
      </div>
    </div>
  );
}

服务端返回的html内容

RSC,如何拆分的客户端与服务端组件
最终页面的呈现

RSC,如何拆分的客户端与服务端组件

 

                                                                                      对于客户端组件与服务端组件的拆分

原则:服务端组件可以包含客户端组件,客户端组件不能包含服务端组件。

服务端组件应该只是纯展示的,没有任何的交互,客户端负责余下的交互部分,感觉服务端组件更像整个页面的layout,

- THE END -
2

共有 0 条评论