
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 标准的前后端表单数据流转链路。