Fetch API 提供了一个 window.fetch() 接口来获取资源(包括网络资源)。Fetch API 可以用来替代 XMLHttpRequest ,可以在 Web Workers 中使用( WorkerGlobalScope.fetch() )。

Fetch API 用法

语法:

1
const fetchResponsePromise = fetch(resource [, init])

resource 参数

fetch() 方法接受一个必填参数 resource ,即要获取的资源(可以是一个字符类型的资源地址,包括 URL 对象,或者是一个 Request 对象)。它返回一个 Promise ,resolve 为对该请求的 Response ,只要服务器返回带 headers 响应——即使服务器响应的是 HTTP 错误状态(比如404 或 500)。如果遇到网络错误(比如权限或者其他问题)则会 reject 并传回 TypeError 。如果被中断,则会 reject 传回 AbortError。

fetch() 方法获取资源的权限由 内容安全策略(Content Security Policy) 的 connect-src 指令控制。

fetch() 的一般用法如下:

Promise用法
1
2
3
4
5
6
7
8
9
10
11
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}).then(res => {
return res.json();
}, err => console.log(err)).then(data => {
console.log(data);
}, err => console.log(err));
async/await用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function getRequestData() {
try {
let res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
let data = await res.json();
console.log(data);
} catch(err) {
console.log(err);
}
}

init 参数

fetch() 方法第二个参数为可选的 init 参数,作为请求的额外参数。

init 选项类似一个 Request 对象,你可以直接使用 Request 构造器创建一个 request。它包括以下选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
method: 'POST', // 请求使用的方法,如 GET, POST, PUT, DELETE 等等.
headers: { // 请求的头信息,可以是一个 Headers 对象或包含 ByteString 值的对象字面量
'Content-Type': 'application/json', // 'Content-Type': 'application/x-www-form-urlencoded',
},
body: undefined, // 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchParams、USVString 或者 ReadableStream 对象。GET 和 HEAD 请求没有 body。
mode: 'cors', // 请求的模式,如 no-cors(请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求), cors(默认值,允许跨域请求), same-origin(只允许同源请求)
cache: 'no-cache', // 请求对 cache 的处理模式,如 default(默认值,先在缓存里面寻找匹配的请求), no-store(直接请求远程服务器,并且不更新缓存), reload(直接请求远程服务器,并且更新缓存), no-cache(将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存), force-cache(缓存优先,只有不存在缓存的情况下,才请求远程服务器), only-if-cached(只检查缓存,如果缓存里面不存在,将返回504错误)
credentials: 'same-origin', // 请求跨域是否带认证信息(cookes 和 HTTP authentication 字段),如 include(一律包括认证信息), same-origin(默认值,同源带认证信息), omit(删掉所有认证信息)
redirect: 'follow', // 请求对重定向的处理模式,如 manual(手动处理), follow(默认值,fetch跟随浏览器对HTTP请求的处理自动重定向), error(如果产生重定向将自动终止并且抛出一个错误)
referrer: 'about:client', // 请求的referrer地址,可以是一个 同源URL 或者 about:client 或留空
referrerPolicy: 'no-referrer', // 指定了 HTTP 的 referer 策略,如 no-referrer(不发送Referer标头), no-referrer-when-downgrade(默认值,总是发送Referer标头,除非从 HTTPS 页面请求 HTTP 资源时不发送), 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标头)
integrity: 'sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=', // 请求的子资源完整性(SRI)值
keepalive: null, // 用于允许请求保活,一个典型场景是用户离开页面的行为统计。带 keepalive 的 Fetch 请求是为了替代 Navigator.sendBeacon()
signal: null, // 一个 AbortSignal 实例,用来通过 AbortController 中止 fetch 请求
}

Headers 对象

init 选项的 headers 可以是一个 Http headers 健值对,也可以为一个 Headers 对象
Fetch API 的 Headers 接口允许对 HTTP 请求和响应头执行各种操作,出于安全考虑,某些 headers 字段 只能由用户代理(浏览器)控制,可以操作的 header 必须是 有效的 HTTP headers 字段
一个 Headers 对象也有一个关联的 guard ,它具有不可变的值,requestrequest-no-corsresponsenone

1
2
3
4
5
6
7
8
9
10
11
12
13
const myHeaders = new Headers({
'Content-Type': 'text/plain',
'Cache-Control': 'no-cache',
'X-Requested-With': 'XMLHttpRequest'
});
myHeaders.append('X-Customer-Header', 'abcd'); // 给现有的 header 添加一个值, 或者添加一个未存在的 header 并赋值
let customerHeader = myHeaders.get('X-Customer-Header'); // 以 ByteString 的形式从 Headers 对象中返回指定 header 的全部值
myHeaders.set('X-Customer-Header', 'efgh'); // 替换现有的 header 的值, 或者添加一个未存在的 header 并赋值
let isCustomerHeaderExisted = myHeaders.has('X-Customer-Header'); // 以 布尔值 的形式从 Headers 对象中返回是否存在指定的 header
myHeaders.delete('X-Customer-Header'); // 从 Headers 对象中删除指定 header
myHeaders.entries(); // 以 迭代器 的形式返回 Headers 对象中所有的键值对
myHeaders.keys(); // 以 迭代器 的形式返回 Headers 对象中所有存在的 header 名
myHeaders.values(); // 以 迭代器 的形式返回 Headers 对象中所有存在的 header 值

Response 对象

Fetch API 的 Response 接口呈现了对一次请求的响应数据。fetch() 方法在网络返回后会 resolve 一个 Response 对象

Response 对象 有以下的属性和方法(Response 实现了 Body 接口,所以也可访问 Body 的属性和方法)

属性/方法 是否只读 含义
Response.headers 只读 包含此 Response 所关联的 Headers 对象。
Response.ok 只读 包含了一个布尔值,标示该 Response 成功(HTTP 状态码的范围在 200-299)。
Response.redirected 只读 表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个条目。
Response.status 只读 包含 Response 的状态码 (例如 200 表示成功)。
Response.statusText 只读 包含了与该 Response 状态码一致的状态信息(例如,OK对应 200)。
Response.type 只读 包含 Response 的类型(例如,basic、cors)。
Response.url 只读 包含 Response 的URL。
Response.useFinalURL 包含了一个布尔值,来标示这是否是该 Response 的最终 URL。
Body.body 只读 一个简单的 getter,用于暴露一个 ReadableStream 类型的 body 内容。
Body.bodyUsed 只读 包含了一个布尔值来标示该 Response 是否读取过 Body。
Response.clone() 创建一个 Response 对象的克隆。
Response.error() 返回一个绑定了网络错误的新的 Response 对象。
Response.edirect() 用另一个 URL 创建一个新的 Response。
Body.arrayBuffer() 读取 Response 对象并且将它设置为已读,并返回一个被解析为 ArrayBuffer 格式的 Promise 对象。
Body.blob() 读取 Response 对象并且将它设置为已读,并返回一个被解析为 Blob 格式的 Promise 对象。
Body.formData() 读取 Response 对象并且将它设置为已读,并返回一个被解析为 FormData 格式的 Promise 对象。
Body.text() 读取 Response 对象并且将它设置为已读,并返回一个被解析为 USVString 格式的 Promise 对象。
Body.json() 读取 Response 对象并且将它设置为已读,并返回一个被解析为 JSON 格式的 Promise 对象。

AbortController 阻止请求

fetch()请求发送以后,如果中途想要取消,需要使用 AbortController 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const controller = new AbortController();
let signal = controller.signal;

const downloadBtn = document.querySelector('.download');
const abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
controller.abort();
console.log('Download aborted');
});

function fetchVideo() {
//...
fetch(url, {signal}).then(function(response) {
//...
}).catch(function(e) {
console.log(e);
})
}

与 XMLHttpRequest 差异

  1. fetch() 使用 Promise,而 XMLHttpRequest 使用回调函数
  2. fetch() 采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象)
  3. fetch() 通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHttpRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性访问

与 jQuery.ajax 差异

  1. 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的 ok 属性为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
  2. fetch() 不会发送跨域 cookies,除非你使用了 credentials 的初始化选项。
    1. 自2018 年 8 月以后,默认的 credentials 政策变更为 same-origin。Firefox 也在 61.0b13 版本中进行了修改。
    2. 如果你打算支持浏览器的旧版本,请确保在所有可能受 Cookie/用户 登录状态影响的api请求上包含credentials: 'same-origin' 初始化选项。