用 fetch 提交表单需拦截 submit 事件并用 FormData 包装数据,不可直接传 form 元素;文件字段自动包含,服务端须解析 multipart/form-data;需禁用按钮防重复提交,并在错误时恢复状态。

用 fetch 提交表单,不刷新页面
直接替换传统 <form method="POST"> 的提交行为,是最轻量、兼容性够用的方案。关键不是“禁用 submit”,而是拦截事件后手动构造数据。
常见错误是直接传 form 元素给 fetch——它不认这个对象。得用 FormData 包装:
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(form);
const res = await fetch('/upload', {
method: 'POST',
body: data // ✅ 直接传 FormData,不用设 Content-Type
});
console.log(await res.json());
});- 文件字段(
<input type="file">)会被自动包含,多选、多个input都没问题 - 服务端收到的是
multipart/form-data,和原生表单提交完全一致 - 不要手动设置
Content-Type头——浏览器会自动生成带 boundary 的正确值,设了反而出错
上传进度监控必须用 XMLHttpRequest
fetch 不支持上传进度事件,这是硬限制。如果需要显示进度条(比如大文件上传),只能切回 XMLHttpRequest。
别写裸的 new XMLHttpRequest(),用封装好的 Promise 版本更可控:
立即学习“前端免费学习笔记(深入)”;
function uploadWithProgress(form) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const data = new FormData(form);
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`上传中:${percent.toFixed(1)}%`);
}
};
xhr.onload = () => resolve(JSON.parse(xhr.response));
xhr.onerror = reject;
xhr.open('POST', '/upload');
xhr.send(data);
});
}-
xhr.upload是唯一能监听上传进度的对象,xhr本身只有下载事件 - 注意区分
xhr.onload(成功)和xhr.onerror(网络失败),500错误属于响应体,仍会进onload - 服务端返回非 JSON 会导致
JSON.parse报错,加try/catch或先检查xhr.status
后端接收时别漏掉 multipart/form-data 解析
前端用 FormData 发,后端必须显式解析 multipart,否则拿不到文件或字段。不同框架处理差异大:
- Node.js + Express:默认不解析,需加
multer中间件,app.use(multer().any()) - Python Flask:
request.files和request.form自动分离,但大文件要调MAX_CONTENT_LENGTH - PHP:
$_FILES和$_POST可直接用,但注意upload_max_filesize配置
一个典型坑:前端传了文件,后端只查 request.body,结果为空——因为文件不在 body 里,而在 multipart 的另一部分。
避免重复提交和按钮状态失控
用户狂点提交按钮是真实场景,光靠前端防抖不够,得结合 UI 状态 + 请求锁。
- 提交瞬间禁用按钮:
button.disabled = true,成功/失败后才恢复 - 用布尔变量标记请求中状态,防止多次触发同一表单的并发请求
- 如果用了
fetch,注意await不会阻止后续代码,禁用逻辑必须在try外层做
最易忽略的是错误恢复:网络中断、服务端 500、JSON 解析失败……这些情况都要重置按钮状态,否则用户以为卡死了。



















