核心是阻止表单默认提交行为,用fetch或XMLHttpRequest发请求并手动处理响应:submit事件中调event.preventDefault(),用FormData提取字段,成功后再更新UI;400错误需检查status并解析errors对象插入对应提示;防重复提交需禁用按钮并设标记;离线时存localStorage,online事件触发重试。

表单提交后页面不跳转,但数据已存到服务器
核心是用 fetch 或 XMLHttpRequest 替代原生 <form> 的默认提交行为。浏览器默认会刷新页面,必须显式阻止它——关键不是“怎么发请求”,而是“怎么不让它刷新”。
常见错误现象:submit 事件没调 event.preventDefault(),结果表单照常提交、页面跳转;或者用了 fetch 但没等响应就清空了表单,用户看不到保存成功反馈。
- 监听
form元素的submit事件,第一时间调用event.preventDefault() - 用
new FormData(formElement)提取所有字段,比手动读input.value更可靠(尤其支持file和多选) - 发送后别急着重置表单,等
fetch().then()确认成功再操作 UI(比如加个绿色对勾图标)
后端返回 400 错误时,如何把校验信息显示在对应输入框下方
前端不能只靠 fetch().catch() 判断失败——HTTP 400 是正常响应,catch 只捕获网络异常。必须检查 response.ok 或 response.status,再解析后端返回的 JSON 校验错误结构。
使用场景:用户邮箱格式不对、密码太短、用户名已被占用……这些都该由后端判断并返回具体字段名和提示文本。
立即学习“前端免费学习笔记(深入)”;
- 后端返回示例结构应为:
{"errors": {"email": "请输入有效的邮箱", "password": "至少6位字符"}} - 前端拿到后遍历
errors对象,用form.querySelector('[name="' + field + '"]')定位输入框,再在其后插入<small class="error">...</small> - 上次提交留下的错误提示要先清除,否则重复提交会叠加多个提示
用户连续点击“保存”按钮,如何防止重复提交
按钮没禁用、请求还在跑、用户又点了一次——后端可能收到两条一模一样的请求,数据库写入重复或触发幂等性问题。这不是 UX 小事,是数据一致性风险。
参数差异:单纯加 disabled 属性最直接,但要注意恢复逻辑——仅在请求完成(无论成功失败)后才启用,不能只在 then 里恢复,catch 也得处理。
- 提交前设
button.disabled = true,同时可改文案为"保存中..." - 请求结束(
finally块)统一恢复按钮状态:button.disabled = false - 更稳妥的做法是在 JS 层加一个标记变量(如
isSubmitting),双重防护避免并发请求
离线状态下点击保存,怎么缓存数据并等网络恢复后自动重发
这不是锦上添花的功能,而是真实场景刚需——用户在地铁里填完表单,出站后希望它自动发出。浏览器原生不提供“请求队列+自动重试”,得自己搭简易机制。
性能影响:本地存储用 localStorage 即可,但注意单条数据别超 5MB;重试时机靠 online 事件触发,别轮询,别用 setInterval。
- 捕获
fetch失败(包括网络断开、CORS、超时),把FormData序列化成 JSON 存进localStorage,键名可用"pending-save-" + Date.now() - 监听
window.addEventListener('online', ...),触发时遍历localStorage找出所有待发项,逐个重试 - 每次成功后删掉对应 key;失败则保留,下次
online再试——但建议加最大重试次数(比如 3 次),避免无限卡住
真正麻烦的是冲突处理:比如用户离线改了姓名,上线后发现服务端版本已被别人更新,这时需要明确提示而非静默覆盖。这个层级的业务逻辑,没法靠通用代码解决。



















