Next.js部署Vercel预渲染报错:ECONNREFUSED 127.0.0.1:3000
问题:Next.js部署到Vercel时主页预渲染失败
本地开发环境运行正常,但部署到Vercel后出现以下错误:
页面"/"预渲染时发生错误。
FetchError: 请求http://localhost:3000/api/getPageInfo失败,原因:connect ECONNREFUSED 127.0.0.1:3000
本地可正常访问http://localhost:3000/api/getPageInfo接口,已在Vercel配置以下环境变量:
NEXT_PUBLIC_BASE_URL=http://localhost:3000NEXT_PUBLIC_SANITY_DATASET=productionNEXT_PUBLIC_SANITY_PROJECT_ID
相关代码片段:
// api/getExperience.ts import type { NextApiRequest, NextApiResponse } from "next"; import { groq } from 'next-sanity'; import { sanityClient } from "../../sanity"; import { Experience } from "../../typings"; const query = groq` *[_type == 'experience'] { ..., technologies[]-> } `; type Data = { experiences: Experience[]; } export default async function handler( req: NextApiRequest, res: NextApiResponse<Data> ) { const experiences: Experience[] = await sanityClient.fetch(query); res.status(200).json({ experiences }) }
// utils/fetchExperiences.ts import { Experience } from '../../typings'; export const fetchExperiences = async() => { const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getExperience`); const data = await res.json() const experiences: Experience[] = data.experiences; return experiences; }
// pages/index.tsx export const getStaticProps: GetStaticProps<Props> = async () => { const pageInfo: PageInfo = await fetchPageInfo(); const experiences: Experience[] = await fetchExperiences(); const skills: Skill[] = await fetchSkills(); const projects: Project[] = await fetchProjects(); const socials: Social[] = await fetchSocials(); return { props: { pageInfo, experiences, skills, projects, socials, }, revalidate: 10, } }
解决方案
错误核心原因
- Vercel构建阶段并未启动
localhost:3000服务,此时通过fetch请求本地API必然失败 NEXT_PUBLIC_BASE_URL硬编码为localhost:3000,生产环境中无法指向正确的Vercel域名- 通过API路由中转Sanity请求属于冗余环节,增加了不必要的网络开销
修复方案
方案1:直接在getStaticProps中调用Sanity客户端(推荐)
跳过API路由层,直接在服务器端的getStaticProps里用sanityClient拉取数据,彻底避免内部请求问题:
// pages/index.tsx import { sanityClient } from '../sanity'; import { groq } from 'next-sanity'; import { PageInfo, Experience, Skill, Project, Social } from '../typings'; // 定义各数据类型的GROQ查询 const pageInfoQuery = groq`*[_type == "pageInfo"][0]`; const experiencesQuery = groq`*[_type == 'experience'] {..., technologies[]-> }`; const skillsQuery = groq`*[_type == 'skill']`; const projectsQuery = groq`*[_type == 'project'] {..., technologies[]-> }`; const socialsQuery = groq`*[_type == 'social']`; export const getStaticProps: GetStaticProps<Props> = async () => { // 直接通过sanityClient获取数据 const pageInfo: PageInfo = await sanityClient.fetch(pageInfoQuery); const experiences: Experience[] = await sanityClient.fetch(experiencesQuery); const skills: Skill[] = await sanityClient.fetch(skillsQuery); const projects: Project[] = await sanityClient.fetch(projectsQuery); const socials: Social[] = await sanityClient.fetch(socialsQuery); return { props: { pageInfo, experiences, skills, projects, socials }, revalidate: 10, } }
方案2:保留API路由时的修正(适合必须保留API层的场景)
区分环境配置BASE_URL
- 本地
.env.local:NEXT_PUBLIC_BASE_URL=http://localhost:3000 - Vercel环境变量:
NEXT_PUBLIC_BASE_URL=https://你的Vercel域名.vercel.app
- 本地
服务器端fetch改用相对路径
Next.js在服务器端执行fetch时,会自动解析相对路径为内部路由,无需依赖环境变量:export const fetchExperiences = async() => { const res = await fetch('/api/getExperience'); const data = await res.json(); return data.experiences; }
额外优化建议
- 若Sanity的项目ID和数据集无需在客户端代码中使用,去掉
NEXT_PUBLIC_前缀,仅在服务器端访问,提升安全性 - 优先选择直接在数据获取函数中调用数据源,减少不必要的API中转环节
内容的提问来源于stack exchange,提问作者Antônio Miguel
相关产品推荐
相关产品推荐

