关于前端:再也不学AJAX了二使用AJAX-②-Fetch-API

1次阅读

共计 5909 个字符,预计需要花费 15 分钟才能阅读完成。

「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,心愿读者通过浏览本系列文章,可能对 AJAX 技术有更加深刻的意识和了解,从此可能 再也不必专门学习 AJAX。本篇文章为该系列的第三篇,最近更新于 2023 年 1 月。

您可能会感觉诧异,在上一篇文章中我向您介绍的 XMLHttpReuqest 对象是 Microsoft 公司在 1999 年提出的,并且是 IE5 的一部分。而在 2014 年,仍然是 Microsoft 公司,提出了咱们现在耳熟能详的 Promise API,并迅速被 TC39 纳入 JavaScript 规范。随后,W3C 颁布了一种更加简洁,优雅的 AJAX 申请 API,这便是本篇文章的配角:「Fetch API」。

1. 什么是 Fetch API

和 XMLHttpRequest 对象一样,Fetch API 是由浏览器提供的,用于获取或操作网络资源的 JavaScript API。它容许开发者通过 JavaScript 代码发送和接管 HTTP 申请和响应。Fetch API 现如今曾经有宽泛的浏览器反对度,在绝大多数状况下都成为 XMLHttpRequest 对象的替代品。

和 XMLHttpRequest 在 Node.js 中须要应用第三方包应用的命运不同,Node 在 2022 年 2 月 1 日将 Fetch API 的 PR 合并, 在 17.5 版本以上的 Node.js 中,Fetch API 都能够间接应用。

2. 上手 Fetch API

2.1 Fetch API 的构造

在上一篇文章中,咱们具体地介绍了如何通过 XHRHttpRequest 对象发送 AJAX 申请。在本章中,咱们将比照 XHRHttpRequest 与 Fetch API 的 API 设计,从而了解为什么 Fetch API 的设计更加优雅和古代。

通过下方的图片,联合上一篇文章解说的内容,您应该很容易了解 XHRHttpRequest API 的整体构造。

Promise 还没有诞生的年代,XHRHttpRequest 对象次要通过事件订阅 / 派发机制,解决申请 / 响应的异步问题。此时所有的必须属性和扩大属性都挂载在 XHRHttpRequest 对象上,并且提供的办法和属性十分底层,很多性能,例如监听响应状态,都须要开发者手动实现。这就催生了例如 axios 等三方包提供语法糖缩小编码的复杂性。

而通过下方的图片,您能够看到 Fetch API 的整体设计要更加简洁,容易了解:

由图中可见,Fetch API 基于 Promise 提供了直观的流式解决办法 .fetch().then().catch()。并且将 AJAX 申请所需的所有信息正当分装在三个类中:RequestHeadersResponse。这种清晰的代码组织形式,使开发者可能十分轻松的了解和应用 Fetch API 的相干个性。

此外,Fetch API 还利用 Promise 个性(.catch() 办法),将错误处理显示的通过接口办法裸露给用户,这使得开发者可能编写出更加持重的代码。

总之,Fetch API 通过联合最新的 JS 异步解决计划 Promise,良好的组织 AJAX 申请数据,办法之间的关系,让 JavaScript 开发者的日子变得更加轻松,并迫使 XHRHttpRequest API 与一系列提供语法糖的第三方库逐步退出历史舞台。

2.2 Fetch API 提供的属性和办法

在理解 Fetch API 的整体设计后,咱们来疾速浏览一遍 Fetch API 提供的外围属性和办法。十分直观的,Fetch API 在全局提供了 fetch() 办法,该办法接管一个 Request 类的实例,或者两个参数:

  1. resource:示意资源获取地址,蕴含任何字符串化后无效的 URL 地址,是 必须 的;
  2. options:蕴含了所有除资源地址外其余自定义 HTTP 申请参数,是 可选 的,比拟罕用的属性有:

    • method:HTTP 申请办法,默认为 GET
    • headers:在此增加申请头部信息;值能够是一个蕴含无效属性的对象,或是一个 Headers 对象的实例(留神这些头部属性无奈被增加);
    • body:任何要向服务端发送的数据,数据类型不限;
    • mode:申请的模式,波及跨域,在前面的文章中咱们会提到;
    • credentials:指定浏览器是否应该在申请中蕴含凭证,也和跨域无关,凭证蕴含 Cookie 和 HTTP 认证,有三个可选值:

      • omit:不蕴含任何凭证;
      • same-origin:仅在申请同源地址时,蕴含凭证(默认值);
      • include:应始终在申请中蕴含凭证;
    • cache:指定了本次申请与浏览器 HTTP 缓存的关系;
    • redirect:指定了如何解决蕴含重定向的响应;

当调用 fetch() 办法后,它会立刻返回一个 Promsie 对象,该 Promise 将在接管到响应头时切换为 resolved,并返回一个 Response 对象的实例。

🚨 留神,兴许和你想的不同,fetch() 办法返回的 Promise 对象只有在网络呈现问题时,才会变更状态为 rejected。也就是说,开发者须要手动查看响应的 HTTP 状态码:

fetch(<url>)
    .then((res) => {if (res.ok) {console.log("ok")
            return res.json()} else {console.log("error")
        }
    })
    .then()
    .catch((err) => {console.log(err)
    })

2.3 Fetch API 实战

在理解到 Fetch API 的根本属性和办法后,我将通过四个经典的示例来介绍如何在我的项目中使用 Fetch API。

2.3.1 发送 POST 申请

应用 Fetch API 发送 POST 申请非常简单,只须要设置 method 属性为 POST,并将申请数据 stringfiy 后增加到 body 属性中即可。

fetch("/api/submit", {
  method: "POST",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({name: "John", age: 30})
})
  .then((res) => {if (res.ok) {return res.json()
    } else {console.log("error")
    }
  })
  .then(data => {console.log("Success:", data);
  })
  .catch(error => {console.error("Error:", error);
  });

2.3.2 解决大文件

能够通过流式解决的形式,解决大文件响应,这使得咱们能够节约更多内存并取得更高性能:

fetch("large-file.jpg")
  .then(response => {const reader = response.body.getReader();
    return new ReadableStream({start(controller) {function push() {reader.read().then(({done, value}) => {if (done) {controller.close();
              return;
            }
            controller.enqueue(value);
            push();});
        }
        push();}
    });
  })
  .then(stream => new Response(stream))
  .then(response => response.blob())
  .then(myBlob => {var objectURL = URL.createObjectURL(myBlob);
    // do something with objectURL
  });

这个例子中,应用 fetch() 办法申请了一个大图片文件,应用 response.body.getReader() 办法获取了一个可读数据流。而后利用 ReadableStream API 将响应数据从流中一段一段的读取进去,并在读取过程中进行解决,这样就能够防止加载整个大文件到内存中,从而解决内存爆炸的问题。

2.3.3 跨域发送申请

应用 Fetch API 发送跨域申请的代码如下:

fetch("https://example.com/data", {
  mode: "cors",
  credentials: "include"
})
  .then((res) => {if (res.ok) {return res.json()
    } else {console.log("error")
    }
  })
  .then(data => {console.log("Success:", data);
  })
  .catch(error => {console.error("Error:", error);
  });

请留神,当您通过向 .fetch() 办法传入的值是 Request 的实例时,mode 的默认值为 cors。当您不须要向服务端传递 cookie 时,能够不设置 credentials 属性。

3. Fetch API vs XHRHttpRequest 对象

在残缺的介绍完 Fetch API 的内容后,是时候站在性能的角度上思考 Fetch API 与 XHRHttpRequest 对象的应用机会了。尽管我之前给出了一个简略的规范:「当不思考老版本浏览器兼容的状况下,始终应用 Fetch API!」然而其实这两者之间,还存在一些性能上的差别。

3.1 Fetch API 个性

3.1.1 更不便的缓存管制

应用 XHRHttpRequest 时,咱们须要编码指定申请头部信息 Cache-Control 管制浏览器的资源缓存行为。然而在 Fetch API 中,咱们能够间接通过在 options 中传入 cache 属性实现这一点,该属性蕴含如下字段:

  • default:优先应用浏览器无效的缓存资源,当缓存资源过期后,向服务器进行条件查问,如果资源过期再发送新的申请;
  • no-store:永远不应用浏览器缓存,间接发送申请,取得的响应不去更新浏览器缓存;
  • reload:永远不应用浏览器缓存,间接发送申请,然而取得响应后更新缓存;
  • no-cache:🚨 留神这里并不意味着「不应用缓存」,而是指每次查看浏览器缓存资源时,无论资源是否过期,都向服务器进行条件查问;
  • force-cache:强制应用浏览器缓存,不论资源是否过期,如果没有缓存资源,再发送申请;
  • only-if-cached:同 force-cache,然而当没有缓存资源时,由浏览器响应 504 状态(该模式仅在 mode: "same-origin" 配置下失效);

3.1.2 更不便的 CORS 管制

在下一章咱们会深刻探讨跨域问题,在 Web 世界,要想拜访不同源下的资源,须要客户端和服务端的全面配合。与缓存管制相似,Fetch API 为咱们提供了 modecredentials 两个配置属性,让开发者可能更轻松的指定申请是否跨域,以及 cookie 等身份信息是否随同申请发送。

credentials 属性蕴含三个可选值:

  • omit:永远不发送或接管 cookie;
  • same-origin:仅当申请地址与以后地址同源时,发送 cookie(默认);
  • include:无论是否同源,均发送 cookie;

3.1.3 更不便的重定向管制

在 XHRHttpRequest 对象时,想要回绝服务器响应的重定向命令,须要通过 xhr.followRedirect = false 指令实现。而在 Fetch API 中,这一项也被设计为一个配置选项 redirect,该属性有三个可选值:

  • follow:听从服务器意见,进行重定向;
  • error:间接抛错,进入 .catch() 流程;
  • manual:回绝重定向,返回服务器信息,由开发者本人管制;

3.1.4 反对更多数据格式

XMLHttpRequest 和 Fetch API 在返回响应实体时都提供了多种数据格式:

  • XMLHttpRequest 提供了几种罕用的数据格式,例如:

    • responseText:返回字符串格局的响应实体;
    • responseXML:返回 XML 格局的响应实体;
    • response:返回 ArrayBuffer, Blob 或 FormData 格局的响应实体;
  • Fetch API 提供了更多数据格式,例如:

    • arrayBuffer():返回 ArrayBuffer 格局的响应实体;
    • blob():返回 Blob 格局的响应实体;
    • formData():返回 FormData 格局的响应实体;
    • json():返回 JSON 格局的响应实体;
    • text():返回字符串格局的响应实体;

3.1.5 反对服务端应用

正如后面提到过的,当 Node 版本大于 17.5 时,就能够间接应用 Fetch API。

3.2 XHRHttpRequest 专属个性

置信看到这里,您曾经可能通过比照上一篇文章介绍的 XHRHttpRequest 对象总结出 Fetch API 所缺失的个性了,没错,相较于 XHRHttpRequest 对象,Fetch API:

  1. 无奈查问申请进度;
  2. 无奈指定申请超时工夫;
  3. 无奈停止申请;
  4. 无奈取得更详尽的响应信息;
  5. 在低版本浏览器中不反对;

然而须要留神,尽管 Fetch API 短少这些性能,但这并不意味着咱们无奈通过其余伎俩实现,例如对于废除申请而言,Fetch API 实际上提供了另一个全局类:AbortController() 来实现这一点:

const controller = new AbortController();

fetch("/service", {
  method: "GET",
  signal: controller.signal,
})
  .then((res) => res.json())
  .then((json) => console.log(json))
  .catch((error) => console.error("Error:", error));

controller.abort();

4. 总结

在本篇文章中,我向您介绍了 Fetch API 方方面面的常识,并通过比照上一篇文章中介绍的 XHRHttpRequest 对象,为您展现了这两个 AJAX API 在设计,内容,编码和性能上的差别。总的来说 XHRHttpRequest 的 API 设计比拟原始,凋谢了更底层的能力,但有良好的浏览器支持性 ,而 Fetch API 则 基于 Promise 对内容做了良好的封装,并对用户裸露了泛滥实用的办法和属性

至此,您应该有能力从容地应用 JavaScript 发送 AJAX 申请,并在将来的 API 设计中使用一些 Fetch API 的设计思维。很快乐您能一路看到这里,在下一篇文章中,我将为您介绍 AJAX 技术的最初一个,但也是最至关重要的主题:「如何跨域申请资源」,敬请期待。

5. 参考资料

  • Fetch API MDN
  • Ajax Battle: XMLHttpRequest vs the Fetch API
  • 为什么有了 XMLHttpRequest,还要设计一套 fetch API?
正文完
 0