前言
在现代前端开发中,Fetch API 已经取代了传统的 XMLHttpRequest,成为浏览器发起 HTTP 请求的标准方式。它基于 Promise 设计,语法简洁,支持 async/await,是每个前端开发者必须掌握的基础技能。
本文将从最基础的 GET 请求开始,一步步带你掌握 POST/PUT/DELETE 等进阶请求,最后教你如何封装一个通用的 Fetch 请求函数,解决原生 Fetch 的所有痛点。
一、Fetch 基础:两种核心写法
Fetch 有两种最常用的写法:.then() 链式调用和 async/await 语法糖,后者是目前项目中的主流写法。
1.1 基础 GET 请求(.then () 写法)
这是 Fetch 最原始的写法,通过 Promise 的链式调用处理异步结果:
javascript
运行
javascript
// 发起 GET 请求获取图书列表
fetch('https://ajax-base-api-t.itheima.net/api/getbooks')
// 第一步:获取响应对象,调用 .json() 解析 JSON 数据
.then(res => {
return res.json();
})
// 第二步:拿到解析后的业务数据
.then(data => {
console.log('图书列表:', data);
})
// 捕获网络错误(注意:404/500 不会进入这里)
.catch(err => {
console.error('请求失败:', err);
});
1.2 基础 GET 请求(async/await 写法)
ES7 引入的 async/await 让异步代码看起来像同步代码,可读性大大提升:
javascript
运行
javascript
// 用 async 标记函数为异步函数
async function getBooks() {
// 用 await 等待 Promise 完成,直接拿到响应对象
const response = await fetch('https://ajax-base-api-t.itheima.net/api/getbooks');
// 等待解析 JSON 数据
const data = await response.json();
console.log('图书列表:', data);
}
// 调用异步函数
getBooks();
二、进阶:发送 POST/PUT/DELETE 请求
Fetch 不止能发 GET 请求,通过第二个配置对象参数,我们可以指定任意请求方法、请求头和请求体。
2.1 POST 请求(提交 JSON 数据)
最常用的场景,比如新增用户、提交表单:
javascript
运行
javascript
async function addBook() {
// 要提交的数据
const bookData = {
bookname: 'JavaScript高级程序设计',
author: '尼古拉斯',
publisher: '人民邮电出版社'
};
const response = await fetch('https://ajax-base-api-t.itheima.net/api/addbook', {
// 指定请求方法
method: 'POST',
// 指定请求头:告诉服务器我发的是 JSON 格式
headers: {
'Content-Type': 'application/json'
},
// 请求体:把 JS 对象转成 JSON 字符串
body: JSON.stringify(bookData)
});
const result = await response.json();
console.log('新增结果:', result);
}
addBook();
2.2 PUT 请求(修改数据)
和 POST 几乎完全一样,只是把 method 改成 PUT:
javascript
运行
php
async function updateBook() {
const response = await fetch('https://ajax-base-api-t.itheima.net/api/updatebook', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: 1,
bookname: '修改后的书名',
author: '修改后的作者'
})
});
const result = await response.json();
console.log('修改结果:', result);
}
2.3 DELETE 请求(删除数据)
通常通过 URL 参数传递要删除的 ID:
javascript
运行
javascript
async function deleteBook(id) {
const response = await fetch(`https://ajax-base-api-t.itheima.net/api/delbook?id=${id}`, {
method: 'DELETE'
});
const result = await response.json();
console.log('删除结果:', result);
}
// 删除 ID 为 1 的图书
deleteBook(1);
三、为什么一定要封装 Fetch?
原生 Fetch 虽然能用,但在实际项目中会暴露出很多问题,封装是必然选择。
3.1 原生 Fetch 的 4 大痛点
- 重复代码太多 :每次请求都要写
method、headers、JSON.stringify()、res.json(),写十次就重复十遍。 - 错误处理反人类 :Fetch 不会把 404/500 当成错误 ,只有网络断开时才会进入
catch,需要手动判断res.ok。 - 无法统一配置:接口域名、请求头、超时时间等如果分散在各个请求里,后期改一处要改几十遍。
- 缺少通用功能:没有内置的请求拦截、响应拦截、全局加载提示、超时取消等功能。
3.2 封装后的好处
- 一行代码发起请求,不用写重复的配置
- 全局统一错误处理,不用每个请求都写
try/catch - 统一基础配置,后期域名变更只改一处
- 业务逻辑和网络请求分离,代码更清晰易维护
四、手把手封装一个通用 Fetch 请求函数
结合实际项目需求,我们来封装一个功能完善、新手友好的 http 函数。
4.1 最终封装代码
javascript
运行
javascript
/**
* 通用 Fetch 请求封装函数
* @param {Object} options 请求配置
* @param {string} [options.method='GET'] 请求方法
* @param {string} options.url 请求地址
* @param {Object} [options.params] URL 查询参数
* @param {Object} [options.data] POST/PUT 请求体数据
* @returns {Promise} 返回解析后的 JSON 数据
*/
async function http(options) {
// 解构参数并设置默认值
const { method = 'GET', url, params, data } = options;
// 1. 处理 URL 查询参数(自动拼接到 URL 末尾)
let fullUrl = url;
if (params && Object.keys(params).length > 0) {
const queryStr = new URLSearchParams(params).toString();
fullUrl = `${fullUrl}?${queryStr}`;
}
// 2. 构造统一的请求配置
const fetchOptions = {
method: method.toUpperCase(), // 统一转大写,避免大小写问题
headers: {
'Content-Type': 'application/json' // 统一设置 JSON 请求头
}
};
// 3. 处理 POST/PUT/PATCH 请求的请求体
if (data && ['POST', 'PUT', 'PATCH'].includes(fetchOptions.method)) {
fetchOptions.body = JSON.stringify(data);
}
// 4. 发起请求 + 统一错误处理
try {
const response = await fetch(fullUrl, fetchOptions);
// 关键:手动判断 HTTP 状态码,404/500 都算失败
if (!response.ok) {
throw new Error(`HTTP 错误:${response.status} ${response.statusText}`);
}
// 解析并返回 JSON 数据
return await response.json();
} catch (err) {
// 全局统一打印错误
console.error('请求失败:', err.message);
// 向外抛出错误,让业务层可以继续处理
throw err;
}
}
4.2 封装函数的使用示例
封装后,发起请求变得极其简洁:
javascript
运行
javascript
// 1. GET 请求(带查询参数)
async function getBookById(id) {
try {
const result = await http({
url: 'https://ajax-base-api-t.itheima.net/api/getbooks',
params: { id }
});
console.log('查询结果:', result);
} catch (err) {
// 业务层可以自定义错误提示
alert('查询图书失败:' + err.message);
}
}
// 2. POST 请求(新增数据)
async function addNewBook() {
try {
const result = await http({
method: 'POST',
url: 'https://ajax-base-api-t.itheima.net/api/addbook',
data: {
bookname: '哇哈哈哈',
author: '大大',
publisher: '江西'
}
});
console.log('新增成功:', result);
// 新增成功后自动刷新列表
getBookList();
} catch (err) {
alert('新增图书失败:' + err.message);
}
}
// 3. GET 请求(无参数)
async function getBookList() {
const result = await http({
url: 'https://ajax-base-api-t.itheima.net/api/getbooks'
});
console.log('所有图书:', result);
}
// 调用函数
getBookById(2);
addNewBook();
五、Fetch 常见坑点总结(新手必看)
await必须和async成对使用 只要函数内部用了await,函数前面必须加async关键字,否则会报语法错误。- Fetch 不会自动拒绝 404/500 错误 这是新手最容易踩的坑,必须手动判断
res.ok,否则 404 也会被当成成功请求。 - URL 参数要用
URLSearchParams处理 不要手动拼接字符串,URLSearchParams会自动处理中文、空格、特殊字符的编码问题。 - 拼写错误是高频问题 常见错误:
fech(少一个 t)、retrun(字母顺序颠倒)、funtion(少一个 c),VS Code 的红色波浪线会提示你。