在现代 Web 开发中,与服务器进行数据交互是几乎每个应用都绕不开的核心环节。过去我们常使用 XMLHttpRequest(XHR),但如今,Fetch API 已成为浏览器原生支持的、更现代、更简洁、更强大的网络请求标准。本文将从基础用法到高级场景,全面解析 Fetch API 的使用技巧,涵盖普通请求、文件上传、文件下载、错误处理、请求中断、进度监控等实用内容。


一、什么是 Fetch API?

Fetch API 是一个用于发起网络请求的 Promise-based Web API,由浏览器原生提供,无需引入第三方库。它旨在替代传统的 XMLHttpRequest,提供更清晰、更灵活的接口。

✅ 优点:语法简洁、基于 Promise、支持 async/await、可与 Service Worker 配合实现离线能力。
⚠️ 注意:Fetch 不会自动处理 HTTP 错误状态码(如 404、500),需手动判断。


二、基础用法

1. GET 请求(获取数据)

1
2
3
4
5
6
7
8
9
fetch('https://api.example.com/users')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json(); // 或 .text(), .blob() 等
})
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));

使用 async/await 更简洁:

1
2
3
4
5
6
7
8
9
10
async function fetchUsers() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) throw new Error('Network response was not ok');
const users = await response.json();
console.log(users);
} catch (error) {
console.error('Failed to fetch:', error);
}
}

2. POST 请求(发送 JSON 数据)

1
2
3
4
5
6
7
8
9
10
11
const userData = { name: 'Alice', email: 'alice@example.com' };

fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
})
.then(res => res.json())
.then(data => console.log('Created:', data));

三、上传文件(File Upload)

上传文件通常使用 FormData 对象,浏览器会自动设置正确的 Content-Type(含 boundary)。

示例:上传单个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<input type="file" id="fileInput" />
<button onclick="uploadFile()">上传</button>

<script>
async function uploadFile() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) return alert('请选择文件');

const formData = new FormData();
formData.append('file', file); // key 为后端接收的字段名
// 也可附加其他字段:formData.append('userId', '123');

try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData // 注意:不要手动设置 Content-Type!
});

if (response.ok) {
const result = await response.json();
console.log('上传成功:', result);
} else {
throw new Error('上传失败');
}
} catch (err) {
console.error('上传出错:', err);
}
}
</script>

🔔 重要提示:使用 FormData 时,不要手动设置 Content-Type 请求头,否则会破坏 multipart boundary,导致后端无法解析。


四、下载文件(File Download)

Fetch 支持下载任意类型文件(图片、PDF、ZIP 等),通过 .blob() 获取二进制数据,再触发浏览器下载。

示例:下载并保存文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
async function downloadFile(url, filename) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error('下载失败');

const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);

const link = document.createElement('a');
link.href = downloadUrl;
link.download = filename; // 指定文件名
document.body.appendChild(link);
link.click();
document.body.removeChild(link);

// 释放内存
window.URL.revokeObjectURL(downloadUrl);
} catch (error) {
console.error('下载出错:', error);
}
}

// 使用
downloadFile('/api/report.pdf', '月度报告.pdf');

💡 提示:若需显示下载进度(见下文),需结合 ReadableStream 或改用 XHR。


五、高级用法与注意事项

1. 错误处理

Fetch 仅在网络故障时 reject Promise,HTTP 错误(如 404、500)仍会 resolve。因此必须检查 response.okresponse.status

1
2
3
4
5
6
7
8
9
fetch('/api/data')
.then(res => {
if (res.status === 401) {
// 处理未授权
} else if (!res.ok) {
throw new Error(`Server error: ${res.status}`);
}
return res.json();
});

2. 设置请求头与凭据

1
2
3
4
5
6
7
fetch('/api/profile', {
headers: {
'Authorization': 'Bearer ' + token,
'X-Custom-Header': 'value'
},
credentials: 'include' // 携带 cookies(跨域时需后端配合)
});

3. 中断请求(AbortController)

1
2
3
4
5
6
7
8
9
10
11
12
13
const controller = new AbortController();
const signal = controller.signal;

fetch('/api/long-task', { signal })
.then(res => res.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求已取消');
}
});

// 5秒后取消
setTimeout(() => controller.abort(), 5000);

4. 超时控制(结合 AbortController)

1
2
3
4
5
6
function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
return fetch(url, { ...options, signal: controller.signal })
.finally(() => clearTimeout(timeoutId));
}

六、Fetch 的局限性

尽管 Fetch 功能强大,但仍有一些不足:

功能 Fetch 支持情况 替代方案
上传/下载进度 ❌(无原生支持) 使用 XHR 或第三方库(如 axios)
自动重试 自行封装或使用库
请求拦截 需手动封装或使用 axios
浏览器兼容性 ✅(现代浏览器) IE 不支持,需 polyfill

📌 对于需要进度条、拦截器等高级功能的项目,axios 仍是更便捷的选择。但对于轻量级、标准化的现代应用,Fetch 完全够用。


七、总结

Fetch API 作为现代 Web 的标准网络接口,具备简洁、强大、原生等优势。掌握其核心用法——包括基础请求、文件上传下载、错误处理、请求中断等——是每个前端开发者的基本功。

✅ 推荐实践:

  • 用 Fetch 替代简单的 XHR 请求;
  • 文件上传用 FormData
  • 下载文件用 blob() + <a download>
  • 始终检查 response.ok
  • 复杂场景可封装成 request 工具函数。

随着 Web 标准的演进,Fetch 正在不断扩展能力(如 fetch() 的流式响应处理),未来将更加强大。拥抱原生,从掌握 Fetch 开始!


参考资料