Next.js App Router服务端Fetch打包报错:localhost连接被拒绝
问题分析
- 报错核心原因:
npm run build执行时,Next.js 会进入静态页面预渲染阶段,此时本地开发服务器(localhost:3000)并未启动,导致服务端代码中fetch("http://localhost:3000/api/posts")无法建立连接,抛出ECONNREFUSED错误。 - 为什么
npm run dev无异常?因为开发模式下本地服务器处于运行状态,服务端的 fetch 请求能正常访问到本地 API 路由。
解决方案
方案1:直接调用API逻辑函数(推荐)
不需要通过 HTTP 请求访问本地 API,直接复用 API 路由里的数据处理逻辑,既消除网络请求开销,也彻底避免构建时的连接问题。
步骤:
- 提取 API 路由中的数据获取逻辑到单独的工具文件,比如
lib/posts.js:
// lib/posts.js // 这里写原来 app/api/posts/route.js 中的数据库查询或数据处理代码 export async function getPosts() { // 示例:假设你用 Prisma 查询数据库 // const posts = await prisma.post.findMany(); // return posts; // 替换成你实际的数据获取逻辑 const mockPosts = [/* 你的数据结构 */]; return mockPosts; }
- 在 Blog 页面中直接调用该函数:
import React from "react"; import styles from "./page.module.css"; import Link from "next/link"; import Image from "next/image"; import { getPosts } from "@/lib/posts"; async function getData() { const posts = await getPosts(); if (!posts) { throw new Error("Failed to fetch data"); } return posts; } const Blog = async () => { const data = await getData(); return ( <div className={styles.mainContainer}> {data.map((item) => ( <Link href={`/blog/${item._id}`} className={styles.container} key={item._id} > <div className={styles.imageContainer}> <Image src={item.img} alt="" width={400} height={250} className={styles.image} /> </div> <div className={styles.content}> <h1 className={styles.title}>{item.title}</h1> <p className={styles.desc}>{item.desc}</p> </div> </Link> ))} </div> ); }; export default Blog;
方案2:强制页面动态渲染
如果必须保留 fetch 请求,可以通过配置让页面跳过预渲染,改为每次请求时动态生成,这样构建阶段不会执行服务端的 fetch 逻辑:
在 Blog 页面顶部添加动态渲染配置:
import React from "react"; import styles from "./page.module.css"; import Link from "next/link"; import Image from "next/image"; // 强制页面为动态渲染,构建时不预渲染 export const dynamic = "force-dynamic"; async function getData() { const res = await fetch("http://localhost:3000/api/posts", { cache: "no-store", }); if (!res.ok) { throw new Error("Failed to fetch data"); } return res.json(); } const Blog = async () => { const data = await getData(); return ( <div className={styles.mainContainer}> {data.map((item) => ( <Link href={`/blog/${item._id}`} className={styles.container} key={item._id} > <div className={styles.imageContainer}> <Image src={item.img} alt="" width={400} height={250} className={styles.image} /> </div> <div className={styles.content}> <h1 className={styles.title}>{item.title}</h1> <p className={styles.desc}>{item.desc}</p> </div> </Link> ))} </div> ); }; export default Blog;
方案3:环境变量区分环境
通过环境变量配置不同环境的 API 地址,开发时用 localhost,生产时用远程服务器地址:
- 在项目根目录创建
.env.local(开发环境):
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000/api
- 生产环境配置
.env文件:
NEXT_PUBLIC_API_BASE_URL=https://your-production-domain.com/api
- 代码中使用环境变量:
async function getData() { const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/posts`, { cache: "no-store", }); if (!res.ok) { throw new Error("Failed to fetch data"); } return res.json(); }
内容的提问来源于stack exchange,提问作者Ders Çalışma zamanı
相关产品推荐
相关产品推荐

