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

Next.js 13.5:静态生成商品页如何实现价格动态获取?

解决方案

方案1:使用客户端组件动态获取价格

这是最直接的实现方式——将价格展示部分封装为客户端组件,在浏览器端发起请求获取实时价格,完全不影响页面其余内容的静态生成逻辑。

步骤:

  1. 创建客户端组件 components/ProductPrice.jsx:
'use client';

import { useEffect, useState } from 'react';

export default function ProductPrice({ slug }) {
  const [price, setPrice] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchPrice = async () => {
      try {
        // 调用后端API获取对应商品的实时价格
        const res = await fetch(`/api/products/${slug}/price`, {
          cache: 'no-store', // 禁用缓存,确保每次请求都拉取最新数据
        });
        if (!res.ok) throw new Error('Failed to fetch price');
        const data = await res.json();
        setPrice(data.price);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchPrice();
  }, [slug]);

  if (loading) return <span>Loading price...</span>;
  if (error) return <span>Error loading price: {error}</span>;
  return <span className="price">${price.toFixed(2)}</span>;
}
  1. 在静态生成的页面组件 app/products/[slug]/page.jsx(默认是服务器组件)中引入该客户端组件:
import ProductPrice from '@/components/ProductPrice';

export async function generateStaticParams() {
  // 从数据库获取所有商品slug,用于构建阶段静态生成页面
  const products = await fetch('/api/products', { cache: 'force-cache' }).then(res => res.json());
  return products.map(product => ({ slug: product.slug }));
}

export default async function ProductPage({ params }) {
  // 获取商品静态信息(名称、描述等),强制缓存用于静态生成
  const product = await fetch(`/api/products/${params.slug}`, { cache: 'force-cache' }).then(res => res.json());

  return (
    <div className="product-page">
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      {/* 价格由客户端组件动态实时获取 */}
      <ProductPrice slug={params.slug} />
      {/* 其他静态内容 */}
    </div>
  );
}
  1. 编写对应的API路由 app/api/products/[slug]/price/route.js:
import { db } from '@/lib/db'; // 替换为你的数据库连接逻辑

export async function GET(request, { params }) {
  const { slug } = params;
  const product = await db.product.findUnique({
    where: { slug },
    select: { price: true },
  });

  if (!product) {
    return new Response(JSON.stringify({ error: 'Product not found' }), { status: 404 });
  }

  return new Response(JSON.stringify({ price: product.price }), {
    headers: { 'Content-Type': 'application/json' },
  });
}

方案2:服务器组件结合动态数据获取(需注意缓存策略)

如果不想依赖客户端组件,可以在服务器组件中单独对价格请求设置无缓存,但这会导致整个页面变为动态渲染(SSR)——其余静态内容仍会在构建时预取,但每次用户访问都会重新获取价格并渲染页面。

修改 app/products/[slug]/page.jsx:

export async function generateStaticParams() {
  const products = await fetch('/api/products', { cache: 'force-cache' }).then(res => res.json());
  return products.map(product => ({ slug: product.slug }));
}

export default async function ProductPage({ params }) {
  // 静态信息使用强制缓存,构建时生成
  const productStatic = await fetch(`/api/products/${params.slug}`, { cache: 'force-cache' }).then(res => res.json());
  // 价格请求禁用缓存,每次访问都获取最新值
  const productPrice = await fetch(`/api/products/${params.slug}/price`, { cache: 'no-store' }).then(res => res.json());

  return (
    <div className="product-page">
      <h1>{productStatic.name}</h1>
      <p>{productStatic.description}</p>
      <span className="price">${productPrice.price.toFixed(2)}</span>
    </div>
  );
}

注意:这种方式会增加服务器负载,适合流量不大的场景,若流量较高建议优先选择方案1。

方案3:增量静态再生(ISR)(适合每日更新场景)

如果价格仅需每日更新,而非实时变更,可以使用ISR让页面在后台自动重新生成,既保留静态页面的性能优势,又能定期同步最新价格。

修改 page.jsx:

export async function generateStaticParams() {
  const products = await fetch('/api/products', { cache: 'force-cache' }).then(res => res.json());
  return products.map(product => ({ slug: product.slug }));
}

// 设置页面每86400秒(24小时)自动重新生成一次
export const revalidate = 86400;

export default async function ProductPage({ params }) {
  // 遵循revalidate缓存策略,24小时后自动重新获取数据
  const product = await fetch(`/api/products/${params.slug}`).then(res => res.json());

  return (
    <div className="product-page">
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <span className="price">${product.price.toFixed(2)}</span>
    </div>
  );
}

这种方式的局限性是价格更新存在延迟,无法满足实时获取最新价格的需求,仅适合固定周期更新的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:22:55