兼容性

image IE11 及以下不行(万恶的 IE)

Fetch polyfill

引入下面这些 polyfill 后可以完美支持 IE8+ :

  1. 由于 IE8 是 ES3,需要引入 ES5 的 polyfill: es5-shim, es5-sham
  2. 引入 Promise 的 polyfill: es6-promise
  3. 引入 fetch 探测库:fetch-detector
  4. 引入 fetch 的 polyfill: fetch-ie8
  5. 可选:如果你还使用了 jsonp,引入 fetch-jsonp
  6. 可选:开启 Babel 的 runtime 模式,现在就使用 async/await

fetch()方法

fetch()方法用于发起获取资源的请求。

参数

第一个参数定义要获取的资源。这可能是:

  • 一个 USVString 字符串,包含要获取资源的 URL。一些浏览器会接受 blob: 和 data: 作为 schemes.
  • 一个 Request 对象。

第二个参数是可选的,是一个配置项对象,包括所有对请求的设置。

  • method: 请求使用的方法,如 GET、POST。
  • headers: 请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量。
  • body: 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
  • mode: 请求的模式,如 cors、 no-cors 或者 same-origin。
  • credentials: 请求的 credentials,如 omit、same-origin 或者 include。为了在当前域名内自动发送 cookie , 必须提供这个选项, 从 Chrome 50 开始, 这个属性也可以接受 FederatedCredential 实例或是一个 PasswordCredential 实例。
  • cache: 请求的 cache 模式: default 、 no-store 、 reload 、 no-cache 、 force-cache 或者 only-if-cached 。
  • redirect: 可用的 redirect 模式: follow (自动重定向), error (如果产生重定向将自动终止并且抛出一个错误), 或者 manual (手动处理重定向). 在 Chrome 中,Chrome 47 之前的默认值是 follow,从 Chrome 47 开始是 manual。
  • referrer: 一个 USVString 可以是 no-referrer、client 或一个 URL。默认是 client。
  • referrerPolicy: Specifies the value of the referer HTTP header. May be one of no-referrer、 no-referrer-when-downgrade、 origin、 origin-when-cross-origin、 unsafe-url 。
  • integrity: 包括请求的 subresource integrity 值 ( 例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。
var myInit = { method: 'GET',
               headers: myHeaders,
               mode: 'cors',
               cache: 'default' };

var myRequest = new Request('flowers.jpg');

fetch(myRequest,myInit).then(function(response) {
  ...
});
1
2
3
4
5
6
7
8
9
10

也可以传入同样的 init 对象到 Request 构造器,来实现同样的效果,如:

var myInit = { method: 'GET',
               headers: {
                   'Content-Type': 'image/jpeg'
               },
               mode: 'cors',
               cache: 'default' };

var myRequest = new Request('flowers.jpg', myInit);
fetch(myRequest).then(function(response) {
  ...
});
1
2
3
4
5
6
7
8
9
10
11

返回值

它返回一个 promise,这个 promise 会在请求响应后被 resolve,并传回 Response 对象。

当遇到网络错误时,fetch() 返回的 promise 会被 reject,并传回 TypeError,虽然这也可能因为权限或其它问题导致。

成功的 fetch() 检查不仅要包括 promise 被 resolve,还要包括 Response.ok 属性为 true。

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });
1
2
3
4
5
6
7

注意,当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 Response.ok 设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。

Request 对象

得到 Request 对象

  • 通过 Request.Request()构造函数创建一个 Request 对象,它的参数和 Fetch()方法的差不多,所以它能代替 Fetch()方法的 init 参数
  • 一个 Request 对象也可能作为其它 API 的操作被返回,比如一个 service worker 的FetchEvent.request

属性

Request 对象的属性都是只读的.

  • Request.method 只读
    • 包含请求的方法 (GET, POST, 等.)
var myRequest = new Request('flowers.jpg');
var myMethod = myRequest.method; // GET
1
2
  • Request.url 只读
    • 包含这个请求的 URL。
var myRequest = new Request('flowers.jpg');
var myURL = myRequest.url; // "http://mdn.github.io/fetch-examples/fetch-request/flowers.jpg"
1
2
  • Request.headers 只读
    • 包含请求相关的 Headers 对象。
var myRequest = new Request('flowers.jpg');
var myHeaders = myRequest.headers; // Headers {}
1
2
  • Request.context 只读
    • 包含请求的上下文(例如:audio, image, iframe, 等)
  • Request.referrer 只读
    • ?包含请求的来源 (例如:client)。
  • Request.referrerPolicy 只读
    • ?包含请求来源的策略 (例如:no-referrer)。
  • Request.mode 只读
    • 包含请求的模式 (例如: cors, no-cors, same-origin, navigate).
  • Request.credentials 只读
    • 包含请求的证书(例如: omit, same-origin,include).
  • Request.redirect 只读
    • 包含?如何处理重定向模式,它可能是一个 follow ,error 或者 manual。
  • Request.integrity 只读
    • 包含请求的子资源的完整性值 (例如: sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=).
  • Request.cache 只读
    • 包含请求的缓存模式 (例如: default, reload, no-cache).

方法

  • Request.clone()
    • 创建当前 request 的副本。
    • clone()TypeError 如果 Body 已经使用了响应,则抛出 a 。实际上,clone()存在的主要原因是允许 Body 对象的多次使用(当它们仅为一次使用时)。
  • Request 实现 Body, 因此它也有 Body 的方法

Response 对象

得到 Response 对象

  • 通过 Response.Response()构造函数创建一个 Response 对象
var myBlob = new Blob();
var init = { status: 200, statusText: 'SuperSmashingGreat!' };
var myResponse = new Response(myBlob, init);
1
2
3

属性

  • Response.type 只读
    • 包含 Response 的类型 (例如, basic, cors).
  • Response.url 只读
    • 包含 Response 的 URL.
  • Response.useFinalURL
    • 包含了一个布尔值来标示这是否是该 Response 的最终 URL.
  • Response.status 只读
    • 包含 Response 的状态码 (例如, 200 成功).
  • Response.ok 只读
    • 包含了一个布尔值来标示该 Response 成功(状态码 200-299) 还是失败.
  • Response.redirected 只读
    • 表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个
  • Response.statusText 只读
    • 包含了与该 Response 状态码一致的状态信息 (例如, OK 对应 200).
  • Response.headers 只读
    • 包含此 Response 所关联的 Headers 对象.
  • Response 实现了 Body, 所以以下属性同样可用:
    • Body.bodyUsed 只读
    • 包含了一个布尔值来标示该 Response 是否读取过 Body.

方法

  • Response.clone()
    • 创建一个 Response 对象的克隆
  • Response.error()
    • 返回一个绑定了网络错误的新的 Response 对象
  • Response.redirect()
    • 用另一个 URL 创建一个新的 response.
  • Response 实现了 Body, 因此它也有 Body 的方法

Headers 对象

Fetch API 的 Headers 接口允许您对 HTTP 请求和响应头执行各种操作。这些操作包括检索,设置,添加和删除。

  • Headers.append()
    • 给现有的 header 添加一个值, 或者添加一个未存在的 header 并赋值.
  • Headers.delete()
    • 从 Headers 对象中删除指定 header.
  • Headers.entries()
    • 以 迭代器 的形式返回 Headers 对象中所有的键值对.
  • Headers.get()
    • 从 Headers 对象中返回指定 header 的第一个值.
  • Headers.getAll()
    • 以数组的形式从 Headers 对象中返回指定 header 的全部值.
  • Headers.has()
    • 以布尔值的形式从 Headers 对象中返回是否存在指定的 header.
  • Headers.keys()
    • 以迭代器的形式返回 Headers 对象中所有存在的 header 名.
  • Headers.set()
    • 替换现有的 header 的值, 或者添加一个未存在的 header 并赋值.
  • Headers.values()
    • 以迭代器的形式返回 Headers 对象中所有存在的 header 的值.
let myHeaders = new Headers();

myHeaders.append('Content-Type', 'text/xml');

myHeaders.get('Content-Type');
// should return 'text/xml'
1
2
3
4
5
6

body 对象

Fetch API 中的 Body mixin 代表 代表响应/请求的正文,允许你声明其内容类型是什么以及应该如何处理。

正文由 Request 和 Response 实现 --- 这为这些对象提供了一个相关联的主体(字节流),一个已使用的标志(最初未设置)和一个 MIME 类型(最初为空字节序列)。

属性

  • Body.bodyUsed 只读
    • 包含一个指示 body 是否被读取过的 Boolean 值。

方法

  • Body.arrayBuffer()
    • 使用一个 buffer 数组来读取 Response 流中的数据,并将 bodyUsed 状态改为已使用。
  • Body.blob()
    • 使用一个 Blob 对象来读取 Response 流中的数据,并将 bodyUsed 状态改为已使用。
  • Body.formData()
    • 使用一个 FormData 对象来读取 Response 流中的数据,并将 bodyUsed 状态改为已使用。
response.formData().then(function(formdata) {
  // do something with your formdata
});
1
2
3
  • Body.json()
    • 使用一个 JSON 对象来读取 Response 流中的数据,并将 bodyUsed 状态改为已使用。
fetch(`https://cdn.xgqfrms.xyz/json/badges.json`)
  .then(response => response.json())
  .then(data => {
    console.log(`data = \n`, data);
  });
1
2
3
4
5
  • Body.text()
    • 使用一个 USVString (文本) 对象来读取 Response 流中的数据,并将 bodyUsed 状态改为已使用。

Fetch 示例

postData('http://example.com/answer', { answer: 42 })
  .then(data => console.log(data)) // JSON from `response.json()` call
  .catch(error => console.error(error));

function postData(url, data) {
  // Default options are marked with *
  return fetch(url, {
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, same-origin, *omit
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer' // *client, no-referrer
  }).then(response => response.json()); // parses response to JSON
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fetch('https://example.com', {
  credentials: 'include'
});
1
2
3

上传文件

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
  .then(response => response.json())
  .catch(error => console.error('Error:', error))
  .then(response => console.log('Success:', response));
1
2
3
4
5
6
7
8
9
10
11
12
13

上传多个文件

var formData = new FormData();
var photos = document.querySelector("input[type='file'][multiple]");

formData.append('title', 'My Vegas Vacation');
formData.append('photos', photos.files);

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData
})
  .then(response => response.json())
  .then(response => console.log('Success:', JSON.stringify(response)))
  .catch(error => console.error('Error:', error));
1
2
3
4
5
6
7
8
9
10
11
12
13

解决超时

const defaultOptions = {
  headers: {
    'Content-Type': 'application/json'
  }
};
function request(url, options = {}) {
  return new Promise((resolve, reject) => {
    const headers = { ...defaultOptions.headers, ...options.headers };
    let abortId;
    let timeout = false;
    if (options.timeout) {
      abortId = setTimeout(() => {
        timeout = true;
        reject(new Error('timeout!'));
      }, options.timeout || 6000);
    }
    fetch(url, { ...defaultOptions, ...options, headers })
      .then(res => {
        if (timeout) throw new Error('timeout!');
        return res;
      })
      .then(checkStatus)
      .then(parseJSON)
      .then(res => {
        clearTimeout(abortId);
        resolve(res);
      })
      .catch(e => {
        clearTimeout(abortId);
        reject(e);
      });
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

感言

其实写到这里,我已经放弃 Fetch 了,ajax 的已经很成熟的优点,例如超时,进度事件.....都已经支持,就为了 ajax 的一些缺点改为 Fetch,却损失了很多 ajax 的缺点,有点丢了西瓜捡芝麻,还是用 axios 吧

TOC