JavaScript Fetch API
学习用 fetch 发送 HTTP 请求 · 难度:进阶 · +15XP
JavaScript Fetch API 完全指南
fetch() 是现代 JavaScript 中发起 HTTP 网络请求的标准方式,已经完全取代了老旧的 XMLHttpRequest。它是浏览器内置的全局函数,基于 Promise 设计,语法简洁而强大。无论是获取 JSON 数据、提交表单、上传文件,还是处理流式响应,fetch 都是前端开发者的核心必备技能。
基本 GET 请求 —— 获取数据
fetch 的第一个参数是 URL,第二个参数是可选的配置对象。它返回一个 Promise,resolve 后得到 Response 对象。注意一个关键点:fetch 只在网络故障时 reject(如断网、DNS 解析失败),HTTP 错误状态码(如 404、500)不会导致 reject,必须手动检查 response.ok 或 response.status。
// 最基础的 GET 请求(使用 Promise 链)
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => {
// 必须先检查响应是否成功
if (!response.ok) {
throw new Error(HTTP错误! 状态码: ${response.status});
}
return response.json(); // 将响应体解析为 JSON,返回 Promise
})
.then(data => {
console.log('文章标题:', data.title);
console.log('文章内容:', data.body);
})
.catch(error => {
console.error('请求失败:', error.message);
});
// async/await 写法(更推荐,像同步代码一样清晰)
async function getPost(id) {
try {
let response = await fetch(
https://jsonplaceholder.typicode.com/posts/${id}
);
if (!response.ok) {
throw new Error(请求失败: ${response.status} ${response.statusText});
}
let data = await response.json();
console.log('获取成功:', data);
return data;
} catch (err) {
console.error('出错了:', err.message);
throw err; // 重新抛出,让调用者也能捕获
}
}
getPost(1);
POST 请求 —— 提交数据
POST 请求需要设置 method 为 'POST',并通过 body 传递数据。发送 JSON 时需设置 Content-Type 头并 JSON.stringify();发送 FormData(如表单、文件)时则不需要手动设置 Content-Type,浏览器会自动处理。
// POST JSON 数据
async function createPost(title, body, userId = 1) {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
body: JSON.stringify({
title: title,
body: body,
userId: userId
})
});
if (!response.ok) throw new Error(状态码: ${response.status});
let data = await response.json();
console.log('创建成功,新文章的ID:', data.id);
return data;
} catch (err) {
console.error('提交失败:', err.message);
}
}
createPost('JavaScript教程', '这是一篇关于Fetch API的文章');
// POST FormData(模拟表单提交或文件上传)
async function uploadFile(file) {
let formData = new FormData();
formData.append('file', file);
formData.append('description', '我的文件');
let response = await fetch('/api/upload', {
method: 'POST',
body: formData
// 注意:不要设置 Content-Type,浏览器会自动设置为
// multipart/form-data 并包含 boundary
});
return response.json();
}
Response 对象 —— 常用属性与方法
| 属性/方法 | 类型 | 说明 |
|---|---|---|
| response.ok | boolean | 状态码 200-299 时为 true,最常用的成功判断 |
| response.status | number | HTTP 状态码:200=成功,404=未找到,500=服务器错误 |
| response.statusText | string | 状态文本:"OK","Not Found","Internal Server Error" |
| response.headers | Headers 对象 | 响应头集合,可用 get() 读取特定头 |
| response.json() | Promise | 将响应体解析为 JSON 对象(最常用) |
| response.text() | Promise | 将响应体解析为纯文本字符串 |
| response.blob() | Promise | 解析为二进制 Blob(图片、文件下载) |
| response.arrayBuffer() | Promise | 解析为 ArrayBuffer(底层二进制数据) |
| response.clone() | Response | 克隆响应对象(响应体只能读取一次) |
// 获取响应头信息
async function inspectResponse(url) {
let response = await fetch(url);
console.log('状态码:', response.status, response.statusText);
console.log('OK?:', response.ok);
console.log('Content-Type:', response.headers.get('Content-Type'));
console.log('Content-Length:', response.headers.get('Content-Length'));
return response;
}
// 注意:响应体只能读取一次!如果需要多次使用请先 clone
async function multiUseResponse(url) {
let response = await fetch(url);
let clone1 = response.clone();
let clone2 = response.clone();
let jsonData = await clone1.json(); // 读取 JSON
let textData = await clone2.text(); // 读取纯文本
// 原始 response 此时已不可用
}
PUT、PATCH、DELETE —— 完整的 CRUD 操作
// PUT —— 完整更新(替换整个资源)
async function updatePost(id, postData) {
let response = await fetch(
https://jsonplaceholder.typicode.com/posts/${id},
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postData)
}
);
return response.json();
}
updatePost(1, { id: 1, title: '新标题', body: '新内容', userId: 1 });
// PATCH —— 部分更新(只更新提供的字段)
async function patchPost(id, partialData) {
let response = await fetch(
https://jsonplaceholder.typicode.com/posts/${id},
{
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(partialData)
}
);
return response.json();
}
// 只更新标题,不传其他字段
patchPost(1, { title: '只修改标题' });
// DELETE —— 删除资源
async function deletePost(id) {
let response = await fetch(
https://jsonplaceholder.typicode.com/posts/${id},
{ method: 'DELETE' }
);
if (response.ok) {
console.log(文章 ${id} 已成功删除);
}
return response.ok;
}
高级特性:超时控制、请求取消、并行请求
// 超时控制 —— 使用 AbortController
async function fetchWithTimeout(url, timeoutMs = 5000) {
let controller = new AbortController();
let timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
let response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) throw new Error(HTTP ${response.status});
return await response.json();
} catch (err) {
if (err.name === 'AbortError') {
throw new Error('请求超时,请检查网络连接后重试');
}
throw err;
}
}
// 并行请求 —— Promise.all 同时发起多个请求
async function fetchAllData() {
try {
let [users, posts, comments] = await Promise.all([
fetch('https://jsonplaceholder.typicode.com/users').then(r => r.json()),
fetch('https://jsonplaceholder.typicode.com/posts').then(r => r.json()),
fetch('https://jsonplaceholder.typicode.com/comments').then(r => r.json())
]);
console.log(用户数: ${users.length});
console.log(文章数: ${posts.length});
console.log(评论数: ${comments.length});
return { users, posts, comments };
} catch (err) {
console.error('数据加载失败:', err.message);
}
}
fetchAllData();
// 三个请求同时发出,总耗时等于最慢的那个,而非三倍!
- 获取并渲染用户列表:使用 fetch 获取 https://jsonplaceholder.typicode.com/users 的数据,将返回的用户数组渲染为一个 HTML 表格,包含 ID、姓名、邮箱、城市四列。用 DOM 操作或 innerHTML 两种方式实现。
- 搜索过滤功能:使用 fetch 获取 https://jsonplaceholder.typicode.com/posts 的数据,在页面上放置一个搜索输入框,根据用户输入的标题关键词实时过滤显示匹配的文章列表。
- 健壮的错误处理:封装一个 robustFetch(url, options) 函数,处理以下所有错误场景:网络断开、请求超时(5秒)、404 页面、500 服务器错误、响应不是合法的 JSON。每种错误都要有对用户友好的中文提示。
- 并行数据整合:用 Promise.all 同时请求 /users、/posts、/comments 三个接口。整合数据后,输出每个用户的姓名、文章数量和收到的评论总数。比如:"Leanne Graham 写了10篇文章,共收到50条评论"。
- 完整的 CRUD 管理界面:针对 https://jsonplaceholder.typicode.com/posts,在网页上实现四个按钮(新增、编辑、删除、刷新)。新增/编辑时弹出简单的 prompt 收集 title 和 body,使用 POST/PUT 发送。删除时弹出确认框,使用 DELETE 请求。所有操作完成后刷新列表。