根本原因是 Axios 默认将响应体一次性加载进内存,真正解决需用 fetch + ReadableStream 绕过 blob 缓存,配合 StreamSaver.js 实现流式下载,全程不经过 JS 内存缓冲。

Vue 中用 Axios 下载大文件时,内存占用高、页面卡顿甚至崩溃,根本原因是默认把整个响应体一次性加载进内存(尤其是 responseType: 'blob')。要真正解决,得绕开 blob 缓存,走浏览器原生流式通道——不是“优化 Axios”,而是让 Axios 退居为请求发起者,把数据流交给更底层的机制处理。
避免 blob 加载,改用 fetch + ReadableStream
Axios 本身不提供浏览器端的流式消费能力(responseType: 'stream' 仅在 Node.js 环境有效)。前端真正能流式下载的,是原生 fetch 返回的 ReadableStream:
- fetch 发起请求后,响应体从网络到达浏览器的第一时间,就能通过
response.body.getReader()分块读取 - 每读一块,就写入
StreamSaver.js创建的可写流(它利用 Service Worker 模拟文件系统写入) - 全程不经过 JavaScript 内存缓冲,文件大小基本不受限(实测 Chrome 下 10GB+ 稳定)
示例代码:
import { createWriteStream } from 'streamsaver';
async function downloadLargeFile(url, filename) {
const response = await fetch(url);
const fileStream = createWriteStream(filename);
const readableStream = response.body;
if (window.WritableStream && readableStream.pipeTo) {
return readableStream.pipeTo(fileStream);
}
// 兜底:手动读取 + 写入
const writer = fileStream.getWriter();
const reader = readableStream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
await writer.write(value);
}
writer.close();
}
保留 Axios 请求逻辑,但只用于发起 + 获取 headers
如果你必须用 Axios(比如已有统一拦截器、鉴权逻辑),可以只让它负责发请求、拿 headers 和 body 流,不碰 data:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
立即学习“前端免费学习笔记(深入)”;
- 设置
responseType: 'stream'在浏览器中会被忽略,所以改用transformRequest拦截并降级为 fetch - 或更稳妥:用 Axios 发送一个轻量 HEAD 请求,提前获取
Content-Length和Content-Disposition(含文件名),再用 fetch 下载主体 - 这样既复用 Axios 的配置体系,又避开它的 blob 内存陷阱
服务端配合要点不能少
流式下载能否生效,后端支持很关键:
- 响应头必须包含
Content-Disposition: attachment; filename="xxx",否则 StreamSaver 无法自动命名 - 推荐加
Content-Transfer-Encoding: binary和Cache-Control: no-cache,避免中间代理缓存或转码 - 如果是 POST 下载(如带参数导出),确保后端返回的是
application/octet-stream或明确 MIME 类型,不要返回 JSON 包裹的 base64
进度反馈要基于流而非总大小
大文件下载时,Content-Length 可能不可靠(如 gzip 压缩后长度变化、动态生成无长度头)。更健壮的做法:
- 监听
reader.read()的每次回调,累加已读 chunk.length - 用
response.headers.get('content-length')做上限参考(如果有),否则只显示“已下载 X MB” - 避免强行算百分比导致进度条跳变或卡死

















