
next.js 的表单组件运行在客户端,而 api 路由(如 `/api/endpoint`)运行在服务端,二者无法通过 `export/import` 共享变量。必须使用 http 请求(如 `fetch` + post)将表单数据序列化后发送至 api 端处理。
在 Next.js 应用中,常见的误区是试图通过模块导出(export let formData)让客户端状态“跨环境”被服务端 API 路由直接读取。这是根本不可行的:React 组件及其状态完全运行在浏览器中(客户端),而 pages/api/xxx.ts 或 app/api/xxx/route.ts 中的 API 处理函数运行在 Node.js 服务端,两者物理隔离、内存不共享、生命周期无关。
✅ 正确做法是:通过标准 HTTP 协议通信——在表单提交时,使用 fetch() 向 API 路由发起 POST 请求,并将表单数据以 JSON 格式作为请求体(body)发送。
以下是完整、可运行的实现方案:
✅ 客户端:优化后的 FormComponent
import { useState } from 'react';
const FormComponent = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
});
const handleChange = (e: React.ChangeEvent) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const res = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const result = await res.json();
console.log('✅ API response:', result);
alert('Message sent successfully!');
} catch (err) {
console.error('❌ Submission failed:', err);
alert('Failed to send. Please try again.');
}
};
return (
);
};
export default FormComponent; ? 关键改进说明:移除全局可变 export let formData(它在模块加载时即固化,且无法响应式更新);使用 useState 管理本地表单状态,确保响应性;handleSubmit 中调用 fetch('/api/contact', {...}),明确指定 POST 方法与 JSON 头;添加错误处理与用户反馈,提升健壮性。
✅ 服务端:对应的 API 路由(pages/api/contact.ts 或 app/api/contact/route.ts)
▪ 若使用 Pages Router(推荐初学者):
创建文件 pages/api/contact.ts:
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
// ✅ 解析 JSON 请求体(Next.js 自动解析,但需确保 Content-Type 正确)
const { name, email, message } = req.body as {
name: string;
email: string;
message: string;
};
console.log('? Received form data:', { name, email, message });
// ? 此处可对接数据库、邮件服务、第三方 API 等
// 示例:await sendEmail({ to: email, subject: `From ${name}`, body: message });
res.status(200).json({
success: true,
received: { name, email, message }
});
} catch (error) {
console.error('? API handler error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}▪ 若使用 App Router(Next.js 13+):
创建 app/api/contact/route.ts:
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
try {
const body = await request.json();
const { name, email, message } = body as {
name: string;
email: string;
message: string;
};
console.log('? App Router received:', { name, email, message });
// ✅ 处理业务逻辑(如存入数据库、触发通知等)
return NextResponse.json(
{ success: true, received: { name, email, message } },
{ status: 200 }
);
} catch (error) {
console.error('? Route handler error:', error);
return NextResponse.json(
{ error: 'Failed to process submission' },
{ status: 500 }
);
}
}⚠️ 注意事项与最佳实践
- 不要依赖模块级变量跨客户端/服务端通信:export const x = ... 在服务端导入时得到的是初始值(甚至可能是 undefined),且每次 API 请求都是全新上下文。
- 始终校验请求方法与数据结构:服务端需检查 req.method === 'POST',并安全解构 req.body(建议类型断言 + try/catch)。
- 前端需设置正确 Content-Type:headers: { 'Content-Type': 'application/json' } 是必需的,否则 Next.js 可能无法自动解析 req.body。
- 生产环境务必添加 CSRF/XSS 防护:对用户输入做服务端校验(如邮箱格式、长度限制)、避免直接执行或渲染未过滤内容。
- 考虑添加加载态与防重复提交:例如禁用按钮、显示 Submitting...,防止用户多次点击。
通过以上方式,你就能在 Next.js 中建立起清晰、可靠、符合 Web 标准的前后端表单数据流转链路。











