如何在Next.js中进行服务端数据获取并保证代码可复用?
Next.js 服务端数据获取与代码复用方案
Next.js 提供了多种服务端数据获取方式,同时可以通过抽离逻辑、组件封装等手段保证代码复用性,以下分两种路由系统详细说明:
Pages Router 方案
1. 核心服务端数据获取方法
getServerSideProps:每次用户请求时在服务端执行,适合需要实时数据的页面getStaticProps:构建时或增量静态再生时在服务端执行,适合静态内容场景
2. 复用数据获取逻辑
把数据请求逻辑抽成独立的异步工具函数,在getServerSideProps/getStaticProps中统一调用,避免重复代码:
// lib/data-fetchers.js export async function fetchUser(userId) { const res = await fetch(`https://api.example.com/users/${userId}`); if (!res.ok) throw new Error('获取用户数据失败'); return res.json(); } export async function fetchUserPosts(userId) { const res = await fetch(`https://api.example.com/users/${userId}/posts`); if (!res.ok) throw new Error('获取用户文章失败'); return res.json(); }
在页面中直接导入使用:
// pages/user/[id].js import { fetchUser, fetchUserPosts } from '../../lib/data-fetchers'; export async function getServerSideProps(context) { const { id } = context.params; // 并行请求提升性能 const [user, posts] = await Promise.all([ fetchUser(id), fetchUserPosts(id) ]); return { props: { user, posts } }; } export default function UserPage({ user, posts }) { return ( <div> <h1>{user.name}</h1> <ul> {posts.map(post => <li key={post.id}>{post.title}</li>)} </ul> </div> ); }
其他页面需要获取用户数据时,直接导入fetchUser即可,无需重复编写请求逻辑。
App Router 方案
App Router 默认使用 Server Components,支持在组件内直接编写异步代码,天然适配服务端数据获取,复用性更强。
1. 异步 Server Component 写法
将带数据获取的UI组件封装成异步组件,可在任意页面复用:
// components/UserProfile.js import { fetchUser } from '@/lib/data-fetchers'; export default async function UserProfile({ userId }) { const user = await fetchUser(userId); return ( <div className="profile-card"> <h2>{user.name}</h2> <p>{user.bio}</p> </div> ); }
在页面中直接引用该组件:
// app/user/[id]/page.js import UserProfile from '@/components/UserProfile'; import { fetchUserPosts } from '@/lib/data-fetchers'; export default async function UserPage({ params }) { const posts = await fetchUserPosts(params.id); return ( <div> <UserProfile userId={params.id} /> <h3>用户文章</h3> <ul> {posts.map(post => <li key={post.id}>{post.title}</li>)} </ul> </div> ); }
2. 客户端组件使用服务端数据
如果需要在 Client Component(带'use client'指令)中使用服务端数据,可通过父级 Server Component 传递 props:
// components/PostList.client.js 'use client'; export default function PostList({ posts }) { // 这里可以添加客户端交互逻辑,比如筛选、排序 return ( <ul> {posts.map(post => <li key={post.id}>{post.title}</li>)} </ul> ); }
在 Server Component 中传入数据:
// app/user/[id]/page.js import PostList from '@/components/PostList.client'; import { fetchUserPosts } from '@/lib/data-fetchers'; export default async function UserPage({ params }) { const posts = await fetchUserPosts(params.id); return <PostList posts={posts} />; }
通用复用技巧
- 统一请求封装:把API请求的公共逻辑(错误处理、请求头、baseURL)抽成工具函数,所有业务请求基于它封装:
// lib/api.js const API_BASE = process.env.NEXT_PUBLIC_API_URL; export async function apiRequest(path, options = {}) { const res = await fetch(`${API_BASE}${path}`, { headers: { 'Content-Type': 'application/json', ...options.headers, }, ...options, }); if (!res.ok) { const errorInfo = await res.json().catch(() => null); throw new Error(errorInfo?.message || `请求失败,状态码:${res.status}`); } return res.json(); } // 业务请求基于上面的函数封装 export async function fetchUser(userId) { return apiRequest(`/users/${userId}`); }
- 缓存重复请求:在App Router中使用
React.cache缓存数据获取函数,避免同一份数据被多次请求:
// lib/data-fetchers.js import { cache } from 'react'; export const fetchUser = cache(async (userId) => { const res = await fetch(`https://api.example.com/users/${userId}`); return res.json(); });
内容的提问来源于stack exchange,提问作者Dinesh V
相关产品推荐
相关产品推荐

