如何实现产品选择后跳转至单个产品页并获取对应产品数据
实现单个产品页获取选中产品数据的方案
基于你当前的Next.js项目结构和代码,以下两种常用方案可实现单个产品页获取对应产品数据:
方案一:组件内动态获取并筛选数据
适合需要实时拉取数据的场景,步骤如下:
- 引入依赖并复用类型定义
import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; // 复用主页面的产品类型接口 interface productItem { id: number; name: string; image: string; price?: string; prices: { half: string; full: string; }; categories: string; }
- 获取路由参数并匹配产品数据
const ProductDetail = () => { const router = useRouter(); const { categories, id } = router.query; const [product, setProduct] = useState<productItem | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { // 路由参数存在时才发起请求 if (categories && id) { const fetchProductDetail = async () => { try { const response = await fetch('/items.json'); const productData = await response.json(); // 根据分类匹配对应产品数组 let targetProducts: productItem[] = []; switch(categories) { case 'Cake': targetProducts = productData.Cakes.map((item: productItem) => ({...item, categories: 'Cake'})); break; case 'Memorella': targetProducts = productData.Memorella.map((item: productItem) => ({...item, categories: 'Memorella'})); break; case 'Ice Cream': targetProducts = productData.Ice_Cream.map((item: productItem) => ({...item, categories: 'Ice Cream'})); break; case 'Pastries': targetProducts = productData.Pastries.map((item: productItem) => ({...item, categories: 'Pastries'})); break; default: throw new Error('无效的产品分类'); } // 筛选出ID匹配的产品 const matchedProduct = targetProducts.find(item => item.id === Number(id)); matchedProduct ? setProduct(matchedProduct) : setError('未找到对应产品'); } catch (err) { setError(err instanceof Error ? err.message : '获取产品数据失败'); } finally { setLoading(false); } }; fetchProductDetail(); } }, [categories, id]); // 处理页面状态 if (loading) return <div>加载产品中...</div>; if (error) return <div>错误:{error}</div>; if (!product) return <div>未找到产品</div>; // 渲染产品详情 return ( <div className="product-detail"> <img src={product.image} alt={product.name} /> <h1>{product.name}</h1> <div className="prices"> <p>半份:₹{product.prices.half}</p> <p>整份:₹{product.prices.full}</p> </div> </div> ); }; export default ProductDetail;
方案二:Next.js静态生成(getStaticProps + getStaticPaths)
适用于产品数据稳定的场景,预生成所有产品页面以提升性能:
- 实现getStaticPaths定义预生成路由
该函数告诉Next.js需要预生成哪些动态路由页面:
export async function getStaticPaths() { const response = await fetch('http://localhost:3000/items.json'); const productData = await response.json(); // 生成所有可能的产品路由路径 const paths = []; Object.entries(productData).forEach(([key, items]) => { let categoryName = ''; switch(key) { case 'Cakes': categoryName = 'Cake'; break; case 'Memorella': categoryName = 'Memorella'; break; case 'Ice_Cream': categoryName = 'Ice Cream'; break; case 'Pastries': categoryName = 'Pastries'; break; } (items as productItem[]).forEach(item => { paths.push({ params: { categories: categoryName, id: item.id.toString() } }); }); }); return { paths, fallback: false // 设为true时,未预生成的路径会在请求时动态生成 }; }
- 实现getStaticProps获取对应产品数据
构建阶段为每个预生成页面获取对应产品数据:
export async function getStaticProps({ params }: { params: { categories: string; id: string } }) { const { categories, id } = params; const response = await fetch('http://localhost:3000/items.json'); const productData = await response.json(); let targetProducts: productItem[] = []; switch(categories) { case 'Cake': targetProducts = productData.Cakes.map((item: productItem) => ({...item, categories: 'Cake'})); break; case 'Memorella': targetProducts = productData.Memorella.map((item: productItem) => ({...item, categories: 'Memorella'})); break; case 'Ice Cream': targetProducts = productData.Ice_Cream.map((item: productItem) => ({...item, categories: 'Ice Cream'})); break; case 'Pastries': targetProducts = productData.Pastries.map((item: productItem) => ({...item, categories: 'Pastries'})); break; default: throw new Error('无效的产品分类'); } const product = targetProducts.find(item => item.id === Number(id)); if (!product) { return { notFound: true }; // 返回404页面 } return { props: { product }, // 传递给组件的属性 revalidate: 86400 // 可选:每隔24小时重新生成页面(增量静态再生) }; }
- 组件接收属性并渲染
const ProductDetail = ({ product }: { product: productItem }) => { return ( <div className="product-detail"> <img src={product.image} alt={product.name} /> <h1>{product.name}</h1> <div className="prices"> <p>半份:₹{product.prices.half}</p> <p>整份:₹{product.prices.full}</p> </div> </div> ); }; export default ProductDetail;
注意事项
- 确保
items.json文件放在Next.js的public文件夹下,才能通过/items.json访问。 - 生产环境中,方案一的fetch路径无需添加
localhost,直接使用/items.json即可。 - 数据频繁更新选方案一,数据稳定选方案二(性能更优)。
内容的提问来源于stack exchange,提问作者Dastagir Shaikh
相关产品推荐
相关产品推荐

