必须用 xmlhttprequest 或 fetch+readablestream 手动提交才能监听上传进度;xmlhttprequest.upload.onprogress 最兼容,需在 open 后、send 前绑定,配合禁用按钮防重复提交。

表单提交时页面卡住,怎么让用户知道“正在上传”
直接用 submit 事件同步提交表单,浏览器会清空当前页面、跳转或刷新,根本没机会显示进度。必须改用 fetch 或 XMLHttpRequest 手动控制提交过程,才能插入进度逻辑。
关键不是“加个 loading 动画”,而是要真正监听上传阶段的字节进度——这只有在使用 fetch 配合 ReadableStream(现代浏览器)或 XMLHttpRequest.upload.onprogress(兼容性更好)时才可行。
- 如果表单只含文本字段,根本不需要进度条,用禁用按钮 + 简单文字提示就够了
- 真正需要进度反馈的,是带
<input type="file">的上传场景 -
fetch默认不暴露上传进度,得靠Request构造时传入自定义body并手动分块读取流,复杂度高 - 对兼容性有要求(比如还要支持 IE11),只能选
XMLHttpRequest
用 XMLHttpRequest 实现文件上传进度监听
XMLHttpRequest 的 upload.onprogress 是最稳定、最直白的进度钩子,它会在每次底层传输一批数据后触发,参数里带 loaded 和 total 字节数。
注意:这个事件只对 upload 对象有效,不是 xhr.onprogress(那是下载进度);且必须在调用 xhr.open() 之后、xhr.send() 之前绑定。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
<p>const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');</p><p>xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
document.getElementById('progress-bar').style.width = <code>${percent}%</code>;
document.getElementById('progress-text').textContent = <code>${percent}%</code>;
}
};</p><p>xhr.onload = () => {
if (xhr.status === 200) {
alert('上传成功');
}
};</p><p>xhr.send(formData);
});</p>
fetch + ReadableStream 的现代写法(Chrome 109+ / Firefox 117+)
新版 fetch 支持通过 Request 的 duplex: 'half' 和手动构造 ReadableStream 来实现上传进度,但限制极多:
- 后端必须支持
Transfer-Encoding: chunked(很多 Node.js 框架默认不启用) - 不能用
FormData直接传,得自己拼boundary和 multipart body -
duplex: 'half'在 Safari 完全不支持,目前仅限实验性场景
简单示例(仅示意结构,实际需处理 boundary 和二进制拼接):
const stream = new ReadableStream({
start(controller) {
const reader = file.stream().getReader();
read();
function read() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value);
// 这里可计算已发送字节数
read();
});
}
}
});
<p>await fetch('/upload', {
method: 'POST',
duplex: 'half',
body: stream,
headers: { 'Content-Type': 'application/octet-stream' }
});</p>
容易被忽略的边界情况
进度条不是加了就完事。真实项目中这几个点经常出问题:
- 用户重复点击提交按钮:必须在
xhr.send()前禁用按钮,并在xhr.onload/xhr.onerror后恢复,否则可能并发发多次请求 - 小文件(setTimeout 强制保持 300ms)
- 移动端 Safari 对
XMLHttpRequest.upload.onprogress触发频率做限制,可能只报告 0% → 100%,需 fallback 到“上传中…”文字提示 - 如果后端用了 Nginx 代理,默认会缓冲整个请求体再转发,导致前端进度始终卡在 0% —— 必须配置
proxy_buffering off;和client_max_body_size
上传进度本质是前后端协同的结果,前端能做的只是“尽可能反映真实状态”,但最终是否准确,取决于后端是否及时透传、中间代理是否干扰、浏览器是否按规范上报。别把进度条当绝对可信指标。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










