Next.js App Router预渲染报错:Vercel部署时API请求失败
问题:Next.js App Router部署Vercel时预渲染页面报错
开发环境运行正常,但部署到Vercel时触发Error occurred prerendering page "/",排查确认是Featured组件的getData方法请求/api/products接口失败——核心原因是构建阶段项目的API路由还未启动服务,无法通过HTTP请求访问。
解决方案
方案1:直接调用API路由的逻辑代码(推荐)
跳过HTTP请求,直接导入API路由里的业务逻辑函数。因为构建阶段同项目的API路由未启动,但代码文件可以正常访问。
假设你的API路由逻辑在app/api/products/route.ts中,并且导出了获取产品的函数(比如getProducts),修改getData方法如下:
// 导入API路由的逻辑函数 import { getProducts } from '@/app/api/products/route'; const getData = async () => { // 直接调用逻辑函数,无需发起HTTP请求 const data = await getProducts(); const validatedProducts = productArraySchema.parse(data); return validatedProducts; };
方案2:强制动态渲染跳过预渲染
如果必须保留HTTP请求逻辑,可以让组件或页面跳过预渲染,改为动态渲染:
- 方法A:在
page.tsx中动态导入组件并禁用SSR
import dynamic from 'next/dynamic'; // 动态导入Featured组件,强制客户端渲染 const Featured = dynamic(() => import('./path-to-featured-component'), { ssr: false, }); const Home = () => { // 原页面代码不变 }; export default Home;
- 方法B:在Featured组件顶部添加动态渲染指令
// 在Featured组件最顶部添加 export const dynamic = 'force-dynamic'; const Featured = async () => { // 原组件代码不变 };
方案3:改用静态生成+重新验证(ISR)
如果希望页面保持静态生成特性,但需要获取最新数据,可以直接从数据源(数据库/第三方API)获取数据,而非调用自身API路由,同时配置重新验证规则:
const getData = async () => { // 直接调用第三方数据源或数据库,跳过自身API路由 const res = await fetch('https://your-external-datasource.com/products', { cache: 'no-store', }); if (!res.ok) { throw new Error("Failed!"); } const data = await res.json(); const validatedProducts = productArraySchema.parse(data); return validatedProducts; };
然后在page.tsx中添加重新验证配置:
// 每60秒重新验证一次数据,更新静态页面 export const revalidate = 60; const Home = () => { // 原页面代码不变 };
修改后的Featured组件示例(方案1)
import { productSchema } from "@/types/types"; import Image from "next/image"; import React from "react"; import { z } from "zod"; import { Button } from "../ui/button"; import { Card, CardTitle, CardContent, CardFooter } from "../ui/card"; // 导入API路由的逻辑函数 import { getProducts } from "@/app/api/products/route"; const getData = async () => { const data = await getProducts(); const validatedProducts = productArraySchema.parse(data); return validatedProducts; }; const productArraySchema = z.array(productSchema); type ProductArrayType = z.infer<typeof productArraySchema>; const Featured = async () => { const featuredProducts: ProductArrayType = await getData(); return ( <div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 mx-auto p-4 max-h-screen overflow-y-scroll"> {featuredProducts.map((item, index) => ( <Card key={index} className="p-1 flex flex-col space-y-1 bg-gradient-to-br from-emerald-500"> <CardTitle className="flex justify-center p-3"> <p>{item.title}</p> </CardTitle> <CardContent className="relative h-40"> <Image src={item.img} fill sizes="(max-width: 600px) 100vw, (max-width: 600px) 50vw, 33vw" alt={item.title} className="rounded-sm absolute inset-0" /> </CardContent> <CardFooter className="flex flex-col items-center space-y-1 justify-center"> <p className="text-sm">{item.desc}</p> <p className="text-md font-semibold"><span className="px-2">price:</span>{item.price}</p> <Button variant="default">Add to cart</Button> </CardFooter> </Card> ))} </div> ); }; export default Featured;
内容的提问来源于stack exchange,提问作者Medhashis Maiti
相关产品推荐
相关产品推荐

