You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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} />;
}

通用复用技巧

  1. 统一请求封装:把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}`);
}
  1. 缓存重复请求:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 07:10:32