React Server Components实战:服务端组件渲染机制与数据获取方案

React Server Components(RSC)是React架构层面的重大演进,将组件渲染分为服务端和客户端两个执行环境。服务端组件在服务器上渲染为序列化数据流发送到客户端,不携带JavaScript运行时代码,直接访问数据库和文件系统。理解RSC的渲染机制和数据获取模式,是React全栈应用开发的核心技能。

React Server Components运行机制详解

RSC将组件树分为Server Component和Client Component两种类型。Server Component以.server.js后缀或在Server端导入的组件自动识别,其代码不打包进客户端bundle。Client Component以"use client"指令声明,代码发送到浏览器执行。

服务端组件与客户端组件的核心差异:

// app/products/page.tsx (Server Component - 默认)
import { db } from '@/lib/database'
import ProductCard from './ProductCard'
import Filter from './Filter'

// 服务端组件可直接async,直接访问数据库
export default async function ProductsPage() {
  const products = await db.query('SELECT * FROM products LIMIT 20')

  return (
    <div>
      {/* Client Component嵌入在Server Component中 */}
      <Filter />
      {products.map(p => (
        <ProductCard key={p.id} product={p} />
      ))}
    </div>
  )
}

// app/products/Filter.tsx
'use client'

import { useState } from 'react'

export default function Filter() {
  const [category, setCategory] = useState('all')
  return (
    <select value={category} onChange={e => setCategory(e.target.value)}>
      <option value="all">全部</option>
      <option value="electronics">电子产品</option>
    </select>
  )
}

// app/products/ProductCard.tsx
// 不带use client,作为Server Component
export default function ProductCard({ product }) {
  return (
    <div className="product-card">
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p>¥{product.price}</p>
    </div>
  )
}

RSC数据获取:fetch缓存策略与Streaming SSR

Next.js App Router基于RSC实现了fetch级别的缓存层。在Server Component中使用fetch时,Next.js自动处理缓存和去重:

// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'

// fetch在Server Component中自动缓存
export default async function BlogPost({ params }: { params: { slug: string } }) {
  const post = await fetch(
    'https://api.example.com/posts/' + params.slug,
    {
      next: {
        revalidate: 3600,           // ISR: 每小时重新验证
        tags: ['blog', 'post-' + params.slug], // 按需重验证标签
      }
    }
  ).then(r => r.json())

  if (!post) notFound()

  // 并行数据获取
  const [author, comments] = await Promise.all([
    fetch('https://api.example.com/users/' + post.authorId, {
      next: { revalidate: 1800, tags: ['author-' + post.authorId] }
    }).then(r => r.json()),
    fetch('https://api.example.com/posts/' + params.slug + '/comments', {
      cache: 'no-store'  // 不缓存,每次请求都获取最新
    }).then(r => r.json())
  ])

  return (
    <article>
      <h1>{post.title}</h1>
      <p>作者: {author.name}</p>
      <div dangerouslySetInnerHTML={{__html: post.content}} />
      <CommentList comments={comments} />
    </article>
  )
}

缓存策略选项:revalidate: N设置ISR间隔;cache: 'no-store'禁用缓存;tags用于按需失效。

Streaming SSR与Suspense边界配置

RSC支持流式渲染,页面内容逐步推送到客户端,优先渲染的内容先展示,慢速数据加载的部分用Suspense占位:

// app/dashboard/page.tsx
import { Suspense } from 'react'
import { Stats, RecentOrders, UserList } from './components'

export default function Dashboard() {
  return (
    <div className="dashboard">
      {/* 快速渲染部分,无Suspense包裹 */}
      <header>仪表盘</header>

      {/* 慢速数据用Suspense包裹,流式推送 */}
      <Suspense fallback={<div className="skeleton">加载统计数据...</div>}>
        <Stats />
      </Suspense>

      <Suspense fallback={<div className="skeleton">加载订单...</div>}>
        <RecentOrders />
      </Suspense>

      {/* 嵌套Suspense,外层先resolve的先展示 */}
      <Suspense fallback={<div className="skeleton">加载用户...</div>}>
        <UserList />
      </Suspense>
    </div>
  )
}

// Stats组件在Server端异步获取数据
async function Stats() {
  const stats = await fetch('https://api.example.com/stats', {
    next: { revalidate: 60 }
  }).then(r => r.json())

  return (
    <div className="stats-grid">
      <div>总订单: {stats.totalOrders}</div>
      <div>总收入: ¥{stats.revenue}</div>
    </div>
  )
}

流式渲染的工作流程:Next.js将RSC树序列化为RSC Payload,Suspense边界将页面切分为多个chunk。已就绪的chunk通过HTTP流式推送,浏览器收到后立即渲染,未就绪的chunk等待数据到达后注入。用户看到的是渐进式页面加载,而非空白等待。

Server与Client组件通信模式

Server Component不能直接向Client Component传递函数,只能传递可序列化数据。需要跨边界传递行为时,使用props传递配置,由Client Component实现交互逻辑:

// Server Component传递props到Client Component
// app/todos/page.tsx (Server)
import TodoApp from './TodoApp'
import { getTodos } from '@/lib/api'

export default async function Page() {
  const initialTodos = await getTodos()
  return <TodoApp initialTodos={initialTodos} />
}

// app/todos/TodoApp.tsx (Client)
'use client'
import { useState } from 'react'

export default function TodoApp({ initialTodos }: { initialTodos: Todo[] }) {
  const [todos, setTodos] = useState(initialTodos)

  const toggleTodo = async (id: number) => {
    const updated = await fetch('/api/todos/' + id, {
      method: 'PATCH',
      body: JSON.stringify({ done: true })
    }).then(r => r.json())
    setTodos(prev => prev.map(t => t.id === id ? updated : t))
  }

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id} onClick={() => toggleTodo(todo.id)}>
          {todo.title} {todo.done ? '✓' : ''}
        </li>
      ))}
    </ul>
  )
}

RSC架构下,初始数据在Server端获取并序列化为props传入Client Component,客户端水合后接管交互。API路由处理后续的状态更新,保持Server Component作为数据入口、Client Component作为交互入口的清晰边界。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/reactservercomponents-shi-zhan-fu-wu-duan-zu-jian-xuan-ran/

(0)
小编小编
上一篇 20小时前
下一篇 20小时前

相关推荐