当前位置: 首页 > article >正文

【前端】Fetch:数据请求

fetch 是一个现代 JavaScript API,用于进行网络请求。它提供了一种简单的接口来获取资源(如数据、文档、图片等),并且支持 Promise 语法,允许开发者通过链式调用来处理请求的结果。

与传统数据请求方式的比较

在 Web 开发中,向服务器发送请求的传统方式有两种:XMLHttpRequestaxios

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 请求

使用 asyncawait 可以让代码更具可读性:

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-urlencodedFormData 格式的请求体。

使用 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 接口如下:

  1. 获取所有图书

    • 接口地址:http://ajax-base-api-t.itheima.net/api/getbooks
    • 请求方式:GET
  2. 添加图书

    • 接口地址:http://ajax-base-api-t.itheima.net/api/addbook
    • 请求方式:POST
    • 请求体参数:
      • bookname: 图书名称
      • author: 作者
      • publisher: 出版社
  3. 删除图书

    • 接口地址: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);
      }
    }
  }
});

代码总结

  1. 渲染图书列表renderBooks 函数使用 fetch 获取数据并动态生成表格 HTML,支持灵活的显示。
  2. 添加图书功能:通过表单提交的 submit 事件捕获输入数据,使用 POST 请求添加数据,并在成功后刷新页面。
  3. 删除图书功能:利用事件委托和 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>

http://www.kler.cn/a/381154.html

相关文章:

  • ROS(Robot Operating System)中,编写一个记录机器人速度并将其转换成轨迹
  • CUDA下载和安装
  • 提交linux kernel patch流程
  • 《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字
  • 通过不当变更导致 PostgreSQL 翻车的案例分析与防范
  • MySQL45讲 第十四讲 count(*)这么慢,我该怎么办?
  • C++之数组和字符串
  • ffplay 实现视频流中音频的延迟
  • 手机ip地址怎么切换外省
  • 【大模型】海外生成式AI赛道的关键玩家:OpenAI、Anthropic之外还有谁?
  • 二、 问题发现(监控工具和方法)
  • 【Unity】Unity拖拽在Android设备有延迟和卡顿问题的解决
  • Qt 视口和窗口
  • 使用RestTemplate发送post请求,入参是多层嵌套的JSON
  • C++优选算法五 位运算
  • SEO
  • UE5相机系统初探(一)
  • 网关(Gateway)和DNS(Domain Name System)
  • 无人机声学侦测算法详解!
  • 构建基于 DCGM-Exporter, Node exporter,PROMETHEUS 和 GRAFANA 构建算力监控系统
  • 【新闻文本分类识别】Python+CNN卷积神经网络算法+深度学习+人工智能+机器学习+文本处理
  • 软考背诵笔记
  • 【植物识别】Python+深度学习+人工智能+CNN卷积神经网络+算法模型训练+TensorFlow
  • WPF+MVVM案例实战与特效(二十五)- 3D粒子波浪效果实现
  • 吉利极氪汽车嵌入式面试题及参考答案
  • 程序员开发速查表