𝑻𝒆𝒏𝑪𝒍𝒂𝒘正在头脑风暴···
𝑻𝒆𝒏𝑲𝒊𝑺𝒆𝒀𝒂の𝑨𝒈𝒆𝒏𝒕助手
𝑻𝒆𝒏-𝒇𝒍𝒂𝒔𝒉

React 服务器组件深度解析

React 服务器组件 (RSC) 是 React 18 的新特性。

什么是服务器组件?

服务器组件是可以在服务器端渲染的组件,默认在服务器上执行。

基础用法

// app/page.tsx - 默认是服务器组件
async function Page() {
const data = await fetch('https://api.example.com/users');
const users = await data.json();

return (
<div>
<h1>用户列表</h1>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}

export default Page;

服务器组件 vs 客户端组件

服务器组件

// 默认是服务器组件
async function ServerComponent() {
const data = await fetch('/api/data');
return <div>{data}</div>;
}

export default ServerComponent;

客户端组件

'use client';

import { useState } from 'react';

function ClientComponent() {
const [count, setCount] = useState(0);

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>
增加
</button>
</div>
);
}

export default ClientComponent;

数据获取

在服务器组件中

async function Page() {
const res = await fetch('https://api.example.com/users', {
cache: 'no-store'
});
const users = await res.json();

return (
<div>
<h1>用户列表</h1>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}

function UserCard({ user }) {
return <div>{user.name}</div>;
}

在客户端组件中

'use client';

import { useState, useEffect } from 'react';

function ClientComponent() {
const [users, setUsers] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setUsers)
.finally(() => setLoading(false));
}, []);

if (loading) return <p>加载中...</p>;

return (
<div>
<h1>用户列表</h1>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}

并发特性

并发数据获取

async function Page() {
const [users, posts] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/posts').then(res => res.json())
]);

return (
<div>
<h1>用户列表</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>

<h1>帖子列表</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}

组件通信

服务器组件传递给客户端组件

async function UserList() {
const users = await fetch('/api/users').then(res => res.json());

return (
<>
{users.map(user => (
<UserProfile key={user.id} user={user} />
))}
</>
);
}

function UserProfile({ user }) {
return <div>{user.name}</div>;
}

客户端组件传递给服务器组件

'use client';

import { useState } from 'react';

function SearchForm() {
const [query, setQuery] = useState('');

return (
<form action="/api/search">
<input
name="q"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button type="submit">搜索</button>
</form>
);
}

// app/page.tsx
async function Page({ searchParams }) {
const query = searchParams?.q || '';

if (query) {
const results = await fetch(`/api/search?q=${query}`).then(res => res.json());
return <div>搜索结果: {results}</div>;
}

return <SearchForm />;
}

缓存策略

缓存控制

async function Page() {
// 默认使用缓存
const data = await fetch('/api/data');

// 完全缓存
const data = await fetch('/api/data', {
cache: 'force-cache'
});

// 每次都重新请求
const data = await fetch('/api/data', {
cache: 'no-store'
});

// 使用缓存但验证
const data = await fetch('/api/data', {
cache: 'force-cache',
next: { revalidate: 60 } // 60秒后重新验证
});

return <div>{data}</div>;
}

客户端 Hooks

使用客户端 Hooks

'use client';

import { useState, useEffect } from 'react';

function ClientComponent() {
const [time, setTime] = useState(new Date());

useEffect(() => {
const timer = setInterval(() => {
setTime(new Date());
}, 1000);

return () => clearInterval(timer);
}, []);

return <p>{time.toLocaleTimeString()}</p>;
}

// 在服务器组件中使用
async function Page() {
const now = new Date();

return (
<div>
<p>服务器时间: {now.toLocaleTimeString()}</p>
<ClientComponent />
</div>
);
}

代码分割

动态导入

import dynamic from 'next/dynamic';

const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <p>加载中...</p>,
ssr: false
});

export default function Page() {
return <HeavyComponent />;
}

Props 传递

传递原始数据

async function PostList() {
const posts = await fetch('/api/posts').then(res => res.json());

return (
<div>
{posts.map(post => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}

function PostCard({ post }) {
return (
<div>
<h2>{post.title}</h2>
<p>{post.content}</p>
</div>
);
}

传递回调函数

'use client';

import { useState } from 'react';

function Counter({ onIncrement }) {
const [count, setCount] = useState(0);

const handleClick = () => {
onIncrement(count);
setCount(c => c + 1);
};

return <button onClick={handleClick}>点击</button>;
}

async function Page() {
const handleIncrement = async (count) => {
const res = await fetch('/api/increment', {
method: 'POST',
body: JSON.stringify({ count })
});
return res.json();
};

return <Counter onIncrement={handleIncrement} />;
}

最佳实践

1. 默认使用服务器组件

// 服务器组件(默认)
async function Page() {
// 数据获取
return <div>内容</div>;
}

// 客户端组件(需要时使用 'use client')
'use client';

2. 数据获取在服务器端

// 好
async function Page() {
const data = await fetch('/api/data');
return <div>{data}</div>;
}

// 不好的做法(客户端获取)
'use client';
function Page() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data').then(setData);
}, []);
return <div>{data}</div>;
}

3. 避免在服务器组件中使用状态

// 好
async function Page() {
return <ClientComponent />;
}

'use client';
function ClientComponent() {
const [state, setState] = useState(0);
return <div>{state}</div>;
}

4. 合理使用缓存

async function Page() {
const data = await fetch('/api/data', {
next: { revalidate: 3600 } // 1小时后重新验证
});
return <div>{data}</div>;
}

5. 错误处理

async function Page() {
try {
const res = await fetch('/api/data');
if (!res.ok) throw new Error('请求失败');
const data = await res.json();
return <div>{data}</div>;
} catch (error) {
return <div>错误: {error.message}</div>;
}
}

总结

服务器组件优势:

  1. 更好的性能:减少客户端 JavaScript 体积
  2. 更少的网络请求:数据直接在服务器获取
  3. 更安全的敏感数据:不会暴露到客户端

使用策略:

  • 默认使用服务器组件
  • 只在需要客户端交互时使用 ‘use client’
  • 合理利用缓存策略

React 服务器组件让前端开发更加高效!