【前端】Fetch:数据请求
fetch
是一个现代 JavaScript API,用于进行网络请求。它提供了一种简单的接口来获取资源(如数据、文档、图片等),并且支持 Promise 语法,允许开发者通过链式调用来处理请求的结果。
与传统数据请求方式的比较
在 Web 开发中,向服务器发送请求的传统方式有两种:XMLHttpRequest
和 axios
。
XMLHttpRequest
使用 XMLHttpRequest
对象通过 Ajax
向服务器请求数据,虽然可以实现功能,但代码显得冗长繁琐。例如:
// 1. 创建一个 XMLHttpRequest 对象
let xhr = new XMLHttpRequest();
// 2. 设置请求方式和地址
xhr.open('get', 'https://api.example.com/data');
// 3. 发送请求
xhr.send();
// 4. 监听 load 事件,获取响应
xhr.addEventListener('load', function () {
console.log(JSON.parse(xhr.response));
});
axios
axios
底层基于 XMLHttpRequest
,但是做了 Promise
的封装,因此代码更简洁:
axios.get('https://api.example.com/data')
.then(response => console.log(response.data))
.catch(error => console.error(error));
fetch
fetch
是现代浏览器原生支持的 API,被称为“下一代 Ajax 技术”。它以 Promise
形式返回结果,语法简洁,易于使用。并且,通过 Stream
(流)来分块读取数据,对大文件请求更为友好。fetch
的兼容性良好,现代浏览器大多支持(IE 除外)。
使用 fetch 发送请求
基本的 GET 请求
fetch
默认发送的是 GET 请求,若只传入 URL 参数,即表示发起一个 GET 请求,返回一个 Promise
对象。
fetch('https://api.example.com/data')
.then(response => {
return response.json(); // 将响应解析为 JSON
})
.then(data => console.log(data))
.catch(error => console.error(error));
fetch
返回的是Response
对象,通过response.json()
方法可将数据解析为 JSON 格式。
使用 async/await 改写 GET 请求
使用 async
和 await
可以让代码更具可读性:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
}
fetchData();
GET 请求带查询参数
GET 请求中,如果需要传递查询参数,可以直接将参数拼接到 URL 中:
async function fetchDataWithParams() {
const url = 'https://api.example.com/data?id=1';
const response = await fetch(url);
const data = await response.json();
console.log(data);
}
fetchDataWithParams();
Response 对象:了解响应的结构
fetch
请求返回的是一个 Response
对象。它包含多个属性和方法,可以用来获取 HTTP 响应的详细信息:
属性 | 说明 |
---|---|
res.ok | 布尔值,表示请求是否成功 |
res.status | 返回 HTTP 状态码(例如:200 表示成功) |
res.url | 返回请求的 URL 地址 |
Response
对象还包含一些常用的方法来解析不同的数据类型:
方法 | 描述 |
---|---|
res.json() | 返回 JSON 对象 |
res.text() | 返回纯文本 |
res.blob() | 返回二进制 Blob 对象 |
res.arrayBuffer() | 返回 ArrayBuffer 对象 |
例如:
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));
发送 POST 请求
对于 POST 请求,通常会传递一些数据。我们可以在 fetch
的第二个参数中配置请求方法、头部信息和请求体:
async function postData() {
const data = {
name: 'John Doe',
age: 30
};
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
const result = await response.json();
console.log(result);
}
postData();
POST 请求支持的不同格式
除了 JSON,fetch
也可以发送 x-www-form-urlencoded
和 FormData
格式的请求体。
使用 x-www-form-urlencoded 格式
async function postFormUrlencoded() {
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body: 'key1=value1&key2=value2',
});
const result = await response.json();
console.log(result);
}
postFormUrlencoded();
使用 FormData 格式
async function postFormData() {
const formData = new FormData();
formData.append('name', 'John');
formData.append('age', '30');
const response = await fetch('https://api.example.com/data', {
method: 'POST',
body: formData,
});
const result = await response.json();
console.log(result);
}
postFormData();
封装 fetch 请求
fetch
原生支持 Promise
,但在实际应用中,我们往往需要更灵活的封装,简化 GET/POST 请求和查询参数处理。以下是一个简单的封装示例:
async function http({ method, url, params, data }) {
// 拼接查询参数
if (params) {
const queryString = new URLSearchParams(params).toString();
url += `?${queryString}`;
}
// 配置请求选项
const options = {
method,
headers: {
'Content-Type': 'application/json'
},
...(data && { body: JSON.stringify(data) }) // 若有 data,则添加请求体
};
const response = await fetch(url, options);
return response.json();
}
使用示例
-
发送 GET 请求
http({ method: 'GET', url: 'https://api.example.com/data', params: { id: 1 } }).then(data => console.log(data));
-
发送 POST 请求
http({ method: 'POST', url: 'https://api.example.com/users', data: { name: 'Alice', age: 28 } }).then(data => console.log(data));
以下是基于fetch
的详细实现教程,包含获取、添加、删除图书功能的详细说明。
实战:图书管理系统
我们将实现一个简单的图书管理系统,主要功能包括获取所有图书、添加图书、删除图书。
前置准备
使用 HTML 表单作为输入界面,通过 fetch
API 向服务器发送请求。服务器的 API 接口如下:
-
获取所有图书:
- 接口地址:
http://ajax-base-api-t.itheima.net/api/getbooks
- 请求方式:
GET
- 接口地址:
-
添加图书:
- 接口地址:
http://ajax-base-api-t.itheima.net/api/addbook
- 请求方式:
POST
- 请求体参数:
bookname
: 图书名称author
: 作者publisher
: 出版社
- 接口地址:
-
删除图书:
- 接口地址:
http://ajax-base-api-t.itheima.net/api/delbook
- 请求方式:
DELETE
- 请求参数:
id
: 要删除图书的 ID
- 接口地址:
渲染图书列表
// 渲染图书列表的函数
const renderBooks = async () => {
try {
// 发起 GET 请求以获取所有图书数据
const response = await fetch('http://ajax-base-api-t.itheima.net/api/getbooks');
const result = await response.json(); // 将响应转换为 JSON 格式
// 检查请求是否成功
if (response.ok) {
// 使用模板字符串构建 HTML 表格内容
const htmlContent = result.data.map(book => `
<tr>
<th scope="row">${book.id}</th>
<td>${book.bookname}</td>
<td>${book.author}</td>
<td>${book.publisher}</td>
<td>
<button type="button" class="btn btn-link btn-sm" data-id="${book.id}">删除</button>
</td>
</tr>
`).join(''); // 使用 join('') 将数组转换为字符串
// 将生成的 HTML 内容插入到页面的表格中
document.querySelector('#tbody').innerHTML = htmlContent;
} else {
alert('获取图书数据失败,请稍后再试!'); // 显示错误提示
}
} catch (error) {
console.error('获取图书列表时出错:', error);
}
};
// 初始化时调用渲染函数
renderBooks();
添加图书功能
// 表单提交事件处理函数,负责添加新图书
document.querySelector('#form').addEventListener('submit', async (e) => {
e.preventDefault(); // 阻止表单的默认提交行为
// 获取表单输入数据
const formData = new FormData(e.target);
const bookData = {
bookname: formData.get('bookname'), // 获取书名
author: formData.get('author'), // 获取作者
publisher: formData.get('publisher') // 获取出版社
};
try {
// 发起 POST 请求以添加新图书
const response = await fetch('http://ajax-base-api-t.itheima.net/api/addbook', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(bookData) // 将图书数据转换为 JSON 格式并发送
});
const result = await response.json(); // 获取服务器响应
if (response.status === 200 || response.status === 201) { // 检查响应状态码,200 表示创建成功
alert('图书添加成功!');
renderBooks(); // 重新渲染图书列表
e.target.reset(); // 重置表单
} else {
alert(`添加图书失败:${result.msg}`); // 显示错误信息
}
} catch (error) {
console.error('添加图书时出错:', error);
}
});
删除图书功能
// 删除按钮点击事件委托,利用事件委托提高性能
document.querySelector('#tbody').addEventListener('click', async (e) => {
// 判断点击的是否为“删除”按钮
if (e.target.tagName === 'BUTTON') {
const bookId = e.target.dataset.id; // 获取图书的 ID
// 弹出确认框,避免误删除
if (bookId && confirm('确认删除这本书吗?')) {
try {
// 发起 DELETE 请求以删除指定 ID 的图书
const response = await fetch(`http://ajax-base-api-t.itheima.net/api/delbook?id=${bookId}`, {
method: 'DELETE'
});
const result = await response.json(); // 获取服务器响应
if (response.ok) { // 检查请求是否成功
alert('图书删除成功!');
renderBooks(); // 删除成功后重新渲染图书列表
} else {
alert(`删除失败:${result.msg}`); // 显示错误信息
}
} catch (error) {
console.error('删除图书时出错:', error);
}
}
}
});
代码总结
- 渲染图书列表:
renderBooks
函数使用fetch
获取数据并动态生成表格 HTML,支持灵活的显示。 - 添加图书功能:通过表单提交的
submit
事件捕获输入数据,使用POST
请求添加数据,并在成功后刷新页面。 - 删除图书功能:利用事件委托和
DELETE
请求删除图书,通过data-id
标识图书。
这样,通过现代化语法和详细注释,代码变得清晰易读、维护简单,适合进一步扩展和学习。
最终代码整合
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图书管理系统</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.min.css">
<style>
.container {
max-width: 800px;
margin-top: 20px;
}
.form-section {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<h2 class="text-center">图书管理系统</h2>
<!-- 图书添加表单 -->
<div class="form-section">
<h4>添加图书</h4>
<form id="form" class="row g-3">
<div class="col-md-4">
<label for="bookname" class="form-label">书名</label>
<input type="text" class="form-control" id="bookname" name="bookname" required>
</div>
<div class="col-md-4">
<label for="author" class="form-label">作者</label>
<input type="text" class="form-control" id="author" name="author" required>
</div>
<div class="col-md-4">
<label for="publisher" class="form-label">出版社</label>
<input type="text" class="form-control" id="publisher" name="publisher" required>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary mt-2">添加图书</button>
</div>
</form>
</div>
<!-- 图书列表展示 -->
<div class="table-responsive">
<h4>图书列表</h4>
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">书名</th>
<th scope="col">作者</th>
<th scope="col">出版社</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody id="tbody">
<!-- 图书数据将动态渲染到这里 -->
</tbody>
</table>
</div>
</div>
<script>
// 渲染图书列表的函数
const renderBooks = async () => {
try {
const response = await fetch('http://ajax-base-api-t.itheima.net/api/getbooks');
const result = await response.json();
if (response.ok) {
const htmlContent = result.data.map(book => `
<tr>
<th scope="row">${book.id}</th>
<td>${book.bookname}</td>
<td>${book.author}</td>
<td>${book.publisher}</td>
<td>
<button type="button" class="btn btn-link btn-sm text-danger" data-id="${book.id}">删除</button>
</td>
</tr>
`).join('');
document.querySelector('#tbody').innerHTML = htmlContent;
} else {
alert('获取图书数据失败,请稍后再试!');
}
} catch (error) {
console.error('获取图书列表时出错:', error);
}
};
// 初始化时调用渲染函数
renderBooks();
// 表单提交事件处理函数,负责添加新图书
document.querySelector('#form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const bookData = {
bookname: formData.get('bookname'),
author: formData.get('author'),
publisher: formData.get('publisher')
};
try {
const response = await fetch('http://ajax-base-api-t.itheima.net/api/addbook', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(bookData)
});
const result = await response.json();
if (response.status === 200 || response.status === 201) {
alert('图书添加成功!');
renderBooks();
e.target.reset();
} else {
alert(`添加图书失败:${result.msg}`);
}
} catch (error) {
console.error('添加图书时出错:', error);
}
});
// 删除按钮点击事件委托
document.querySelector('#tbody').addEventListener('click', async (e) => {
if (e.target.tagName === 'BUTTON') {
const bookId = e.target.dataset.id;
if (bookId && confirm('确认删除这本书吗?')) {
try {
const response = await fetch(`http://ajax-base-api-t.itheima.net/api/delbook?id=${bookId}`, {
method: 'DELETE'
});
const result = await response.json();
if (response.ok) {
alert('图书删除成功!');
renderBooks();
} else {
alert(`删除失败:${result.msg}`);
}
} catch (error) {
console.error('删除图书时出错:', error);
}
}
}
});
</script>
</body>
</html>