
联系表单必须包含哪些 input 字段
用户能发出去、你能收得到,是最低要求。缺了关键字段,后续所有验证和提交都是空谈。
-
name:别用username或contact_name,后端解析时容易不一致;统一用name最稳妥 -
email:必须设type="email",浏览器会做基础格式校验,且移动端自动弹出邮箱键盘 -
subject:建议用select+ 默认选项,避免用户乱填“你好”“test”“???”导致归类困难 -
message:必须是textarea,不是input type="text";后者撑不开,用户写两行就看不见自己输的内容 - 隐藏字段
honeypot(如website)加在表单末尾,CSS 设为display: none,真实用户不会填,爬虫和垃圾 bot 常常会填——后端收到非空值直接拒收
提交失败时,fetch 报错但页面没提示
这是最常被忽略的交互断点:表单点了没反应,用户以为发成功了,其实请求根本没发出去,或 500 了也没反馈。
- 不要只监听
fetch().then(),必须加.catch()处理网络异常(比如 CORS、离线、服务不可达) - 后端返回非 2xx 状态码(如 400 表单校验失败、500 服务器错误),
fetch不会进catch,得手动判断response.ok === false - 错误提示别塞进
alert(),改用页面内div浮层或字段下方small文本;否则用户关掉弹窗就忘了哪错了 - 示例关键逻辑:
fetch('/contact', { method: 'POST', body: formData }) .then(r => { if (!r.ok) throw new Error(r.status + ' ' + r.statusText); return r.json(); }) .catch(err => { document.querySelector('.error-message').textContent = '发送失败,请检查网络或稍后重试'; });
action 和 method 还在用传统跳转提交?
纯 HTML 提交(method="POST" + action="/contact")看似简单,实际问题一堆:刷新丢数据、无法控制加载态、错误无法局部反馈、SEO 友好但体验差。
- 现代做法是禁用默认提交:
form.addEventListener('submit', e => { e.preventDefault(); /* 自己 fetch */ }) -
action属性可以留空或设为#,它已不承担功能,仅作语义占位 - 如果真要保留传统提交(比如 CMS 插件限制),务必加
novalidate属性,并确保后端返回完整 HTML 页面(含原输入值 + 错误提示),否则用户一出错就丢失所有填写内容 - 注意:某些老旧邮件网关(如 PHPMailer 直连 SMTP)不支持 JSON 提交,此时仍需
application/x-www-form-urlencoded格式,用new URLSearchParams(formData)构造 body
为什么本地测试时 fetch 提交总报 CORS 错误
不是代码写错了,是开发环境本身没配代理或没起服务,浏览器直接拦掉了。
立即学习“前端免费学习笔记(深入)”;
- 静态文件双击打开(
file://协议)下,任何fetch都会触发 CORS,这是浏览器安全策略,无解——必须走http://localhost:xxxx - Vite / Webpack Dev Server 默认不代理 POST 请求到后端,需显式配置:Vite 中加
server.proxy['/contact'] = { target: 'http://localhost:3000', changeOrigin: true } - 后端没设
Access-Control-Allow-Origin响应头?别只加*,带凭证(如 cookie)时必须指定具体域名,且要加Access-Control-Allow-Credentials: true - 常见假象:Chrome 控制台报 CORS,但 Network 面板里请求状态是 200 —— 实际是预检(OPTIONS)失败,后端根本没收到 POST 数据
表单的复杂不在字段多,而在每个环节都可能静默失败:前端没捕获网络异常、后端没返回可读错误、CORS 配置漏掉预检、甚至 honeypot 字段被 CSS 错误隐藏导致正常用户也填不了。这些点不一个个对齐,用户点十次“发送”,你一条消息都收不到。











