推荐参看链接:

接受一个 URL 字符串作为参数,默认向网址发送 GET 请求,返回一个 Promise 对象

  • fetch() 接收到的 response 是一个 Stream 对象
  • response.json() 是一个异步操作,将受到的数据转化为 JSON 对象

    1. fetch('https://api.github.com/users/mcx2020')
    2. .then(response => response.json())
    3. .then(json => console.log(json))
    4. .catch(error => console.log('Request Failed', error));

    await 写法

  • await 放到 try…catch… 里面,这样可以捕捉到异步操作中发生的错误

    1. async function getJSON() {
    2. let url = 'https://api.github.com/users/ruanyf';
    3. try {
    4. let response = await fetch(url);
    5. return await response.json();
    6. } catch (error) {
    7. console.log('Request Failed', error);
    8. }
    9. }

二、Response 对象

Response 包含的数据通过 Stream 接口异步读取,它还包含一些同步属性

  • ok:返回一个布尔值,表示请求是否成功(true对应 HTTP 请求的状态码 200 到 299,false 对应其他)
  • status:返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)
  • statusText:返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回”OK”)
  • url:返回请求的 URL(如果 URL 存在跳转,该属性返回的是最终 URL)
  • type:返回请求的类型
    • basic:普通请求,即同源请求
    • cors:跨域请求
    • error:网络错误,主要用于 Service Worker
    • opaque:如果fetch()请求的type属性设为no-cors,就会返回这个值
    • 表示发出的是简单的跨域请求,类似
      表单的那种跨域请求。
    • opaqueredirect:如果fetch()请求的redirect属性设为manual,就会返回这个值
  • redirected:返回一个布尔值,表示请求是否发生过跳转

判断请求是否成功

  • 只有网络错误或无法连接时,fetch() 才会报错,其他情况都不会报错
  • 只有通过 response.status 和 response.ok 判断是否请求成功

三、Response.header 属性

Response.header 属性,指向一个 Header 对象,对应 HTTP 回应的所有标头,可通过如下方式遍历它的所有属性

  1. const response = await fetch(url);
  2. for (let [key, value] of response.headers) {
  3. console.log(`${key} : ${value}`);
  4. }
  5. // 或者
  6. for (let [key, value] of response.headers.entries()) {
  7. console.log(`${key} : ${value}`);
  8. }
  1. // response.headers.get(),用于读取某个标头的值用于读取某个标头的值
  2. let response = await fetch(url);
  3. response.headers.get('Content-Type')
  4. // application/json; charset=utf-8
  1. // Headers.keys()和Headers.values()方法用来分别遍历标头的键名和键值
  2. // 键名
  3. for(let key of myHeaders.keys()) {
  4. console.log(key);
  5. }
  6. // 键值
  7. for(let value of myHeaders.values()) {
  8. console.log(value);
  9. }

四、读取 response 对象的数据

下面 5个读取方法都是异步的,返回的都是 Promise 对象,必须等到异步操作结束,才能得到完整数据

  • response.text():得到文本字符串
  • response.json():得到 JSON 对象
  • response.blob():得到二进制 Blob 对象
  • response.formData():得到 FormData 表单对象
  • response.arrayBuffer():得到二进制 ArrayBuffer 对象

response.text()可以用于获取文本数据,比如 HTML 文件

  1. const response = await fetch('/users.html');
  2. const body = await response.text();
  3. document.body.innerHTML = body

response.blob()用于获取二进制文件(下面示例读取图片文件flower.jpg,显示在网页上)

  1. const response = await fetch('flower.jpg');
  2. const myBlob = await response.blob();
  3. const objectURL = URL.createObjectURL(myBlob);
  4. const myImage = document.querySelector('img');
  5. myImage.src = objectURL;

response.arrayBuffer()主要用于获取流媒体文件(下面示例是response.arrayBuffer()获取音频文件song.ogg,然后在线播放的例子)

  1. const audioCtx = new window.AudioContext();
  2. const source = audioCtx.createBufferSource();
  3. const response = await fetch('song.ogg');
  4. const buffer = await response.arrayBuffer();
  5. const decodeData = await audioCtx.decodeAudioData(buffer);
  6. source.buffer = buffer;
  7. source.connect(audioCtx.destination);
  8. source.loop = true;

五、Response.body 属性

Response.body属性是 Response 对象暴露出的底层接口,返回一个 ReadableStream 对象,它可以用来分块读取内容,应用之一就是显示下载的进度(下面示例中,response.body.getReader()方法返回一个遍历器。这个遍历器的read()方法每次返回一个对象,表示本次读取的内容块。 这个对象的done属性是一个布尔值,用来判断有没有读完;value属性是一个 arrayBuffer 数组,表示内容块的内容,而value.length属性是当前块的大小。)

  1. const response = await fetch('flower.jpg');
  2. const reader = response.body.getReader();
  3. while(true) {
  4. const {done, value} = await reader.read();
  5. if (done) {
  6. break;
  7. }
  8. console.log(`Received ${value.length} bytes`)
  9. }

六、fetch 定制 HTTP 请求

fetch()的第一个参数是 URL,还可以接受第二个参数,作为配置对象,定制发出的 HTTP 请求
(HTTP 请求的方法、标头、数据体都在这个对象里面设置)

  1. fetch(url, optionObj)

POST 请求(有些标头不能通过headers属性设置,比如Content-Length、Cookie、Host等等。它们是由浏览器自动生成,无法修改)

  1. const response = await fetch(url, {
  2. method: 'POST',
  3. headers: {
  4. "Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
  5. },
  6. body: 'foo=bar&lorem=ipsum',
  7. });
  8. const json = await response.json();

请求 JSON 数据(下面示例中,标头Content-Type要设成’application/json;charset=utf-8’。因为默认发送的是纯文本,Content-Type的默认值是’text/plain;charset=UTF-8’)

  1. const user = { name: 'John', surname: 'Smith' };
  2. const response = await fetch('/article/fetch/post/user', {
  3. method: 'POST',
  4. headers: {
  5. 'Content-Type': 'application/json;charset=utf-8'
  6. },
  7. body: JSON.stringify(user)
  8. });

提交表单

  1. const form = document.querySelector('form');
  2. const response = await fetch('/users', {
  3. method: 'POST',
  4. body: new FormData(form)
  5. })

文件上传(如果表单里面有文件选择器,可以用前一个例子的写法,上传的文件包含在整个表单里面,一起提交。另一种方法是用脚本添加文件,构造出一个表单,进行上传)

  1. const input = document.querySelector('input[type="file"]');
  2. const data = new FormData();
  3. data.append('file', input.files[0]);
  4. data.append('user', 'foo');
  5. fetch('/avatars', {
  6. method: 'POST',
  7. body: data
  8. });

直接上传二进制数据(fetch()也可以直接上传二进制数据,将 Blob 或 arrayBuffer 数据放在body属性里面。)

  1. let blob = await new Promise(resolve =>
  2. canvasElem.toBlob(resolve, 'image/png')
  3. );
  4. let response = await fetch('/article/fetch/post/image', {
  5. method: 'POST',
  6. body: blob
  7. });

七、fetch() 配置对象的完整 API

fetch() 第二个参数的完整 API 如下

  1. const response = fetch(url, {
  2. method: "GET",
  3. headers: {
  4. "Content-Type": "text/plain;charset=UTF-8"
  5. },
  6. body: undefined,
  7. referrer: "about:client",
  8. referrerPolicy: "no-referrer-when-downgrade",
  9. mode: "cors",
  10. credentials: "same-origin",
  11. cache: "default",
  12. redirect: "follow",
  13. integrity: "",
  14. keepalive: false,
  15. signal: undefined
  16. });

cache属性指定如何处理缓存。可能的取值如下

  • default:默认值,先在缓存里面寻找匹配的请求
  • no-store:直接请求远程服务器,并且不更新缓存
  • reload:直接请求远程服务器,并且更新缓存
  • no-cache:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存
  • force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器
  • only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误

mode属性指定请求的模式。可能的取值如下:

  • cors:默认值,允许跨域请求
  • same-origin:只允许同源请求
  • no-cors:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求

credentials属性指定是否发送 Cookie。可能的取值如下:

  • same-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送
  • include:不管同源请求,还是跨域请求,一律发送 Cookie
  • omit:一律不发送

signal属性指定一个 AbortSignal 实例,用于取消fetch()请求

keepalive属性用于页面卸载时,告诉浏览器在后台保持连接,继续发送数据(一个典型的场景就是,用户离开网页时,脚本向服务器提交一些用户行为的统计信息。这时,如果不用keepalive属性,数据可能无法发送,因为浏览器已经把页面卸载了)

  1. window.onunload = function() {
  2. fetch('/analytics', {
  3. method: 'POST',
  4. body: "statistics",
  5. keepalive: true
  6. });
  7. };

redirect属性指定 HTTP 跳转的处理方法。可能的取值如下:

  • follow:默认值,fetch()跟随 HTTP 跳转
  • error:如果发生跳转,fetch()就报错
  • manual:fetch()不跟随 HTTP 跳转,但是response.url属性会指向新的 URL,response.redirected属性会变为true,由开发者自己决定后续如何处理跳转

integrity属性指定一个哈希值,用于检查 HTTP 回应传回的数据是否等于这个预先设定的哈希值( 比如,下载文件时,检查文件的 SHA-256 哈希值是否相符,确保没有被篡改)

  1. fetch('http://site.com/file', {
  2. integrity: 'sha256-abcdef'
  3. });

referrer属性用于设定fetch()请求的referer标头。这个属性可以为任意字符串,也可以设为空字符串(即不发送referer标头)

  1. fetch('/page', {
  2. referrer: ''
  3. });

referrerPolicy属性用于设定Referer标头的规则。可能的取值如下:

  • no-referrer-when-downgrade:默认值,总是发送Referer标头,除非从 HTTPS 页面请求 HTTP 资源时不发送
  • no-referrer:不发送Referer标头
  • origin:Referer标头只包含域名,不包含完整的路径
  • origin-when-cross-origin:同源请求Referer标头包含完整的路径,跨域请求只包含域名
  • same-origin:跨域请求不发送Referer,同源请求发送
  • strict-origin:Referer标头只包含域名,HTTPS 页面请求 HTTP 资源时不发送Referer标头
  • strict-origin-when-cross-origin:同源请求时Referer标头包含完整路径,跨域请求时只包含域名,HTTPS 页面请求 HTTP 资源时不发送该标头
  • unsafe-url:不管什么情况,总是发送Referer标头。

八、取消 fetch 请求

fetch()请求发送以后,如果中途想要取消,需要使用AbortController对象
下面的示例的流程如下

  1. 新建一个 AbortController 实例
  2. 发送 fetch() 请求,其中配置对象的 signal 属性需要指定为 AbortController 实例的 controller.signal
  3. 监听 controller 的 abort 事件,在此可以书写取消后的逻辑
  4. controller.abort() 用于发出取消信号,会触发 abort 事件
  5. controller.signal.aborted 属性判断取消信号是否已经发出 ```javascript let controller = new AbortController(); let signal = controller.signal;

fetch(url, { signal: controller.signal });

signal.addEventListener(‘abort’, () => console.log(‘abort!’) );

controller.abort(); // 取消

console.log(signal.aborted); // true

  1. 下面是一个1秒后自动取消请求的例子
  2. ```javascript
  3. let controller = new AbortController();
  4. setTimeout(() => controller.abort(), 1000);
  5. try {
  6. let response = await fetch('/long-operation', {
  7. signal: controller.signal
  8. });
  9. } catch(error) {
  10. if (error.name == 'AbortError') {
  11. console.log('Aborted!');
  12. } else {
  13. throw error;
  14. }
  15. }

「@浪里淘沙的小法师」