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

NextJS App Router动态路由中如何向页面组件传递对象?

解决Next.js App Router动态路由传递Post对象的问题

在Next.js App Router中,动态路由/posts/[id]无法直接传递完整的Post对象,因为路由仅能捕获URL中的参数(比如id)。针对你提到的“每次详情页加载整个JSON文件筛选导致性能差”的问题,以下是几种可行的优化方案:


方案一:服务器组件预取单个Post数据(推荐)

App Router的服务器组件支持直接在组件内异步获取数据,你可以根据id仅请求单个Post的数据,避免加载整个列表。

修改./app/posts/[id]/page.tsx

type Post = {
  id: number;
  title: string;
  likes: number;
};

interface PostPageProps {
  params: { id: string };
}

export default async function PostPage({ params }: PostPageProps) {
  // 方式1:从API请求单个Post数据
  const res = await fetch(`/api/posts/${params.id}`, { cache: 'force-cache' });
  const post = await res.json();

  // 方式2:读取单个本地JSON文件(服务器端可用)
  // import fs from 'fs/promises';
  // const postData = await fs.readFile(`public/posts/${params.id}.json`, 'utf-8');
  // const post = JSON.parse(postData);

  if (!post) return <div>文章不存在</div>;

  return (
    <div>
      <h1>{post.title}</h1>
      <p>点赞数: {post.likes}</p>
    </div>
  );
}

说明:服务器组件会在构建时(静态生成)或请求时(SSR)获取单个数据,既避免了加载整个列表的性能损耗,也符合App Router的设计模式,是最优解。


方案二:客户端状态管理(React Context)

如果需要在客户端传递已加载的列表数据,可以用React Context存储列表,详情页直接从Context中根据id匹配数据。

步骤1:创建PostContext组件

'use client';

import { createContext, useContext, useState, ReactNode } from 'react';
import type { Post } from '../types';

interface PostContextType {
  posts: Post[];
  setPosts: React.Dispatch<React.SetStateAction<Post[]>>;
}

const PostContext = createContext<PostContextType | undefined>(undefined);

export function PostProvider({ children }: { children: ReactNode }) {
  const [posts, setPosts] = useState<Post[]>([]);
  return (
    <PostContext.Provider value={{ posts, setPosts }}>
      {children}
    </PostContext.Provider>
  );
}

export function usePosts() {
  const context = useContext(PostContext);
  if (!context) {
    throw new Error('usePosts必须在PostProvider内部使用');
  }
  return context;
}

步骤2:在根布局中包裹Provider

修改./app/layout.tsx:

import { PostProvider } from './components/PostContext';

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="zh-CN">
      <body>
        <PostProvider>{children}</PostProvider>
      </body>
    </html>
  );
}

步骤3:列表页存入数据到Context

修改./app/posts/page.tsx:

'use client';

import { usePosts } from '@/components/PostContext';
import Link from 'next/link';
import { useEffect, useState } from 'react';

type Post = {
  id: number;
  title: string;
  likes: number;
};

export default function Page() {
  const { setPosts, posts } = usePosts();
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function fetchPosts() {
      try {
        const res = await fetch('/api/posts');
        const data = await res.json();
        setPosts(data);
      } catch (err) {
        setError('加载文章失败');
      } finally {
        setLoading(false);
      }
    }

    fetchPosts();
  }, [setPosts]);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>{error}</p>;

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <ul className="text-3xl font-bold">
        {posts.map((post) => (
          <li key={post.id}>
            <Link href={`/posts/${post.id}`} className="block">
              {post.title}
            </Link>
          </li>
        ))}
      </ul>
    </main>
  );
}

步骤4:详情页从Context读取数据

修改./app/posts/[id]/page.tsx:

'use client';

import { usePosts } from '@/components/PostContext';

interface PostPageProps {
  params: { id: string };
}

export default function PostPage({ params }: PostPageProps) {
  const { posts } = usePosts();
  const post = posts.find(p => p.id === parseInt(params.id));

  // 处理直接访问详情页的情况(此时Context无数据)
  if (!post) {
    // 可选: fallback 到请求单个数据
    return <div>加载中...</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>点赞数: {post.likes}</p>
    </div>
  );
}

说明:适合客户端渲染场景,但需处理用户直接访问详情页(如刷新)时Context无数据的情况,可 fallback 到方案一的单个数据请求。


方案三:URL查询参数(不推荐,仅适用于小数据)

将Post的字段作为查询参数附加到URL,详情页从searchParams中读取。

<Link 
  href={{
    pathname: `/posts/${post.id}`,
    query: { title: post.title, likes: post.likes.toString() }
  }} 
  className="block"
>
  {post.title}
</Link>

详情页接收参数

interface PostPageProps {
  params: { id: string };
  searchParams: { title?: string; likes?: string };
}

export default function PostPage({ params, searchParams }: PostPageProps) {
  const post = {
    id: parseInt(params.id),
    title: searchParams.title || '',
    likes: parseInt(searchParams.likes || '0')
  };

  return (
    <div>
      <h1>{post.title}</h1>
      <p>点赞数: {post.likes}</p>
    </div>
  );
}

说明:实现简单但数据会暴露在URL中,不适合复杂对象,且参数长度有浏览器限制,仅作为临时备选方案。


内容的提问来源于stack exchange,提问作者KSI

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 11:28:22