JavaScript Fetch API 完全指南
从零掌握 Fetch API,学会 GET/POST/文件上传/错误处理/超时控制。 · 难度:入门 · +15XP
告别 XMLHttpRequest
在 Fetch 出现之前,前端发送 HTTP 请求用的是 XMLHttpRequest(简称 XHR)。它的 API 设计得……不太友好:
// XHR 写法(老方式)
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');
xhr.onload = () => console.log(JSON.parse(xhr.responseText));
xhr.onerror = () => console.error('失败了');
xhr.send();
而 Fetch API 用 Promise 包装,写法简洁得多:
// Fetch 写法(现代方式)
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error('失败了', err));
GET 请求:获取数据
最基本的用法——从服务器拿数据:
async function getUsers() {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('HTTP Error: ' + response.status);
}
const users = await response.json();
console.log(users); // [{id:1, name:'Leanne'}, ...]
}
关键点:response.ok 只在 HTTP 状态码 200-299 时为 true。Fetch 不会把 404 或 500 当作错误——你必须自己检查!
response 对象的常用方法
| 方法 | 返回 | 用途 |
|---|---|---|
response.json() | Promise<Object> | 解析 JSON 响应体 |
response.text() | Promise<string> | 读取纯文本 |
response.blob() | Promise<Blob> | 读取图片/文件 |
response.formData() | Promise<FormData> | 读取表单数据 |
response.status | number | 状态码(200/404/500) |
response.ok | boolean | 状态码是否 200-299 |
POST 请求:发送数据
async function createUser(name, email) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, email }),
});
const result = await response.json();
console.log('创建成功:', result);
}
上传文件的正确方式
上传文件不要设 Content-Type,让浏览器自动处理 multipart/form-data 和 boundary:
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('folder', 'avatars');
const response = await fetch('/api/upload', {
method: 'POST',
body: formData, // 不用设 Content-Type!
});
return response.json();
}
超时控制(Fetch 不自带超时)
Fetch 没有内置的超时机制,但你可以用 AbortController 实现:
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timer);
return response;
} catch (err) {
clearTimeout(timer);
if (err.name === 'AbortError') {
throw new Error('请求超时');
}
throw err;
}
}
取消请求
用户快速切换页面时,上一个请求可能还在进行中。用 AbortController 取消它:
const controller = new AbortController();
// 发起请求
fetch('/api/search?q=react', { signal: controller.signal });
// 用户输入了新关键词,取消上一个请求
controller.abort();
这在搜索框实时查询(debounce + abort)场景中非常实用。
实战:封装一个通用的请求函数
async function request(url, options = {}) {
const defaultOptions = {
headers: { 'Content-Type': 'application/json' },
timeout: 10000,
};
const mergedOptions = { ...defaultOptions, ...options };
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), mergedOptions.timeout);
try {
const response = await fetch(url, {
...mergedOptions,
signal: controller.signal,
});
if (!response.ok) {
const errorBody = await response.text().catch(() => '');
throw new Error('HTTP ' + response.status + ': ' + errorBody.slice(0, 100));
}
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return response.json();
}
return response.text();
} finally {
clearTimeout(timer);
}
}
// 使用
const users = await request('/api/users');
const newUser = await request('/api/users', {
method: 'POST',
body: JSON.stringify({ name: '小明' }),
});
动手试试
- 基础练习:用 Fetch 从
https://jsonplaceholder.typicode.com/posts获取数据,展示前 10 条标题 - 进阶应用:用上面封装的 request 函数,实现一个带超时和错误重试(失败后自动重试 3 次)的请求工具
- 项目实战:把你项目中现有的 axios 调用改成 Fetch,封装成和项目 API 风格一致的请求函数,保证错误处理逻辑不变
接下来学什么?
学会了 Fetch,下一课我们学习 JavaScript Promise 从入门到精通——理解 async/await 的底层原理、错误传播、并发控制。
📖 译自 MDN Web Docs Using the Fetch API (CC-BY-SA 2.5),有改编和扩充。