详解 H5 新增的 Fetch API

591 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

详解 H5 新增的 Fetch API

1.Fetch API 出现的原因

在过去我们通常是使用XMLHttpRequest对象与服务器交互,从而实现 AJAX 异步请求,但XMLHttpRequest 存在以下问题:

  1. 所有的功能全部集中在同一个对象上,容易书写出混乱且不易维护的代码
  2. 采用传统的事件驱动模式,无法适配 ES6 提出的Promise

为了解决以上问题,官方就提出了新 API-Fetch,它主要有以下特点:

  1. 并非取代 AJAX,而是对 AJAX 传统 API 的改进
  2. 精细的功能分割:头部信息、请求信息、响应信息等均分布到不同的对象,更利于处理各种复杂的 AJAX 场景
  3. 使用 Promise API,更利于异步代码的书写
  4. Fetch API 并非 ES6 的内容,属于 HTML5 新增的 Web API
  5. 需要掌握网络通信的知识

2.基本使用

使用 fetch 函数即可立即向服务器发送网络请求

const url = "请求地址";
fetch(url); //已发送请求,返回一个Promise对象
/*fetch(url,{
  //请求参数对象
  method: 'POST',
})*/

2.1 参数

该函数有两个参数:

  1. 必填,字符串,请求地址
  2. 选填,对象,请求配置

请求配置对象的可选参数

  • method:字符串,请求方法,默认值 GET

  • headers:对象,请求包含的 Headers 对象,详细介绍见第五章

  • body: 请求体的内容,必须匹配请求头中的 Content-Type

  • mode:字符串,请求模式,能解决跨域问题

    • cors:默认值,配置为该值,会在请求头中加入 origin 和 referer
    • no-cors:配置为该值,不会在请求头中加入 origin 和 referer,跨域的时候可能会出现问题
    • same-origin:指示请求必须在同一个域中发生,如果请求其他域,则会报错
  • credentials: 如何携带凭据(cookie)

    • omit:默认值,不携带 cookie
    • same-origin:请求同源地址时携带 cookie
    • include:请求任何地址都携带 cookie
  • cache:配置缓存模式

    • default: 表示 fetch 请求之前将检查下 http 的缓存。
    • no-store: 表示 fetch 请求将完全忽略 http 缓存的存在。这意味着请求之前将不再检查下 http 的缓存, 拿到响应后, 它也不会更新 http 缓存。
    • no-cache: 如果存在缓存, 那么 fetch 将发送一个条件查询 request 和一个正常的 request, 拿到响应后, 它会更新 http 缓存。
    • reload: 表示 fetch 请求之前将忽略 http 缓存的存在, 但是请求拿到响应后, 它将主动更新 http 缓存。
    • force-cache: 表示 fetch 请求不顾一切的依赖缓存, 即使缓存过期了, 它依然从缓存中读取。 除非没有任何缓存, 那么它将发送一个正常的 request。
    • only-if-cached: 表示 fetch 请求不顾一切的依赖缓存, 即使缓存过期了, 它依然从缓存中读取。 如果没有缓存, 它将抛出网络错误(该设置只在 mode 为”same-origin”时有效)。

重点是前三个属性 —— method、headers、body 的配置,是我们平时开发经常需要配置的属性。

2.2 返回值

fetch 函数返回一个 Promise 对象

  • 收到服务器的返回结果(与响应状态码无关)后,Promise 进入 resolved 状态,状态数据为 Response 对象
  • 网络发生错误(或其他导致无法完成交互的错误)时,Promise 进入 rejected 状态,状态数据为错误信息

3.Request 对象

除了使用基本的 fetch 方法,还可以通过创建一个 Request 对象来完成请求(实际上,fetch 的内部会帮你创建一个 Request 对象)

如以下代码:

const myRequest = new Request(url地址, 请求配置对象); //先创建一个 Request 对象
const resp = await fetch(myRequest); //fetch发送请求 resp为一个 Response 对象

Request 对象的参数与 fetch 函数的参数一致,使用详细介绍见第一章 fetch 方法参数介绍。

注意点:

尽量保证每次请求都是一个新的 Request 对象(post 请求上传大型文件时导致重复上传失败),此时应需调用 Request 对象clone方法赋值一个全新的 Request 对象,新对象配置与之前的一致。

4.Response 对象

一般并不会手动创建 Response 对象,只有测试时才会创建 Response 对象,因为当 fetch 方法返回的 Promise 进入 resolved 状态,返回的数据就是 Response 对象。

//const myResponse = new Response(响应体数据, 响应配置对象);
const resp = new Response(
  `[
    {"id":1, "name":"北京"},
    {"id":2, "name":"天津"}
  ]`,
  {
    ok: true,
    status: 200,
  }
);

4.1 Response 对象的主要属性

  • body:响应体,用于存放响应数据,实现为 ReadableStream
  • bodyUsed: boolean,表示响应体内容是否已被读取
  • headers: 响应包含的 Headers 对象
  • ok:boolean,表示 HTTP 状态码的含义,200~299 的状态码返回 true,其他状态码返回 false redirected: boolean,表示响应是否至少经过一次重定向
  • status:number,表示响应的 HTTP 状态码,默认为 200
  • statusText: string,包含对 HTTP 状态码的正式描述
  • type: string,包含响应类型。
    • basic:表示标准的同源响应
    • cors:表示标准的跨源响应
    • error:表示响应对象是通过 Response.error()创建的
    • opaque:表示 no-cors 的 fetch()返回的跨源响应
    • opaqueredirect:表示对 redirect 设置为 manual 的请求的响应
  • url: 包含响应 URL 的字符串。对于重定向响应,这是最终的 URL,非重定向响应就是它产生的

4.2 Response 对象的主要方法

  • text():用于处理文本格式的 Ajax 响应。它从响应中获取文本流,将其读完,然后返回一个被解决为 string 对象的 Promise 对象
  • blob():用于处理二进制文件格式(比如图片或者电子表格)的 Ajax 响应。它读取文件的原始数据,一旦读取完整个文件,就返回一个被解决为 blob 对象的Promise 对象
  • json():用于处理 JSON 格式的 Ajax 的响应。它将 JSON 数据流转换为一个被解决为 JavaScript 对象的Promise 对象
  • redirect():可以用于重定向到另一个 URL。它会创建一个新的 Promise,以解决来自重定向的 URL 的响应。
  • error():用于产生表示网络错误的 Response 对象(网络错误会导致 fetch()返回的 Promise 对象被拒绝)。
  • clone():用于会创建一个一模一样的副本,不会覆盖任何值,在响应体被读取之后再克隆会导致抛出 TypeError。(有响应体的 Response 对象只能读取一次)

5.Headers 对象

在 Request 和 Response 对象内部,会将传递的请求头对象,转换为 Headers 对象

使用方法:

const myHeaders = new Headers({
  a: 1,
  b: 2,
}); // 创建一个 Headers 对象
const myRequest = new Request(url地址, {
  headers,
});
const resp = await fetch(myRequest); // resp为一个 Response 对象

5.1 Headers 对象的主要属性

  • Accept:浏览器可以处理的内容类型。
  • Accept-Charset:浏览器可以显示的字符集。
  • Accept-Encoding:浏览器可以处理的压缩编码类型。
  • Accept-Language:浏览器使用的语言。
  • Connection:浏览器与服务器的连接类型,如:keep-alive表示长连接
  • Cookie:页面中设置的 Cookie。
  • Host:发送请求的页面所在的域。
  • Referer:发送请求的页面的 URI。由于该字段在 HTTP 规范中就拼错了,所以将错就错。(正确的拼写应是 Referrer。)
  • User-Agent:浏览器的用户代理字符串。

5.2 Headers 对象的主要方法

  • has(key):检查请求头中是否存在指定的 key 值
  • get(key): 得到请求头中对应的 key 值
  • set(key, value):修改对应的键值对
  • append(key, value):添加对应的键值对
  • keys(): 得到所有的请求头键的集合,可通过 for-of 遍历
  • values(): 得到所有的请求头中的值的集合,可通过 for-of 遍历
  • entries(): 得到所有请求头中的键值对的集合,可通过 for-of 遍历

6.fetch API 实现文件上传

通过 fetch API 使用 AJAX 技术进行无刷新文件上传。

上传流程:

  1. 客户端将文件数据发送给服务器
  2. 服务器保存上传的文件数据到服务器端
  3. 服务器响应给客户端一个文件访问地址

请求方法:POST(因为文件数据量较大) 请求的表单格式:multipart/form-data 请求体中必须包含一个键值对,键的名称是服务器要求的名称,值是文件数据

HTML5 中,JS 仍然无法随意的获取文件数据,但是可以获取到 input 元素中,被用户选中的文件数据 可以利用 HTML5 提供的 FormData 构造函数来创建请求体

如上传图片:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>

  <body>
    <img src="" alt="" id="imgAvatar" />
    <input type="file" id="avatar" />
    <button>上传</button>
    <script>
      async function upload() {
        const inp = document.getElementById('avatar');
        if (inp.files.length === 0) {
          alert('请选择要上传的文件');
          return;
        }
        const formData = new FormData(); //构建请求体
        formData.append('imagefile', inp.files[0]);//键名为服务器所规定的键名
        const url = '服务器地址';//请求地址
        const resp = await fetch(url, {
          method: 'POST',
          body: formData, //自动修改请求头
        });
        const result = await resp.json();
        return result;
      }

      document.querySelector('button').onclick = async function () {
        const result = await upload();
        const img = document.getElementById('imgAvatar');
        img.src = result.path;
      };
    </script>
  </body>
</html>

结语

这是我目前所了解的知识面中最好的解答,当然也有可能存在一定的误区。

所以如果对本文存在疑惑,可以在评论区留言,我会及时回复的,欢迎大家指出文中的错误观点。

最后码字不易,觉得有帮助的朋友点赞、收藏、关注走一波。