导读

Fetch 是 web 异步通信的未来。从chrome42,Firefox39,Opera29,EdgeHTML14(并非Edge版本)起,fetch就已经被支持了。其中chrome42~45版本,fetch对中文支持有问题,建议从chrome46起使用fetch。

Fetch

先过一遍Fetch原生支持率。

Fetch - caniuse
Fetch - 浏览器原生支持情况

可见要想在IE8/9/10/11中使用fetch还是有些犯难的,毕竟它连 Promise 都不支持,更别说fetch了。别急,这里有polyfill(垫片)。

由于IE8基于ES3,IE9支持大部分ES5,IE11支持少量ES5,其中只有IE10对ES5支持比较完整。因此IE8+浏览器,建议依次装载上述垫片。

尝试一个fetch

先来看一个简单的fetch。

1
2
3
4
5
6
7
8
9
10
var word = '123',
url = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+word+'&json=1&p=3';

fetch(url,{mode: "no-cors"}).then(function(response) {
return response;
}).then(function(data) {
console.log(data);
}).catch(function(e) {
console.log("Oops, error");
});

fetch执行后返回一个 Promise 对象,执行成功后,成功打印出 Response 对象。

Fetch 执行后返回的 Response 对象

该fetch可以在任何域名的网站直接运行,且能正常返回百度搜索的建议词条。以下是常规输入时的是界面截图:

百度搜索Suggestion

以下是刚才fetch到的部分数据。其中 key name 为 s 的字段的 value 就是以上的建议词条(由于有高亮词条 12306,最后一条数据 12366 被顶下去了,故上面截图上看不到)

Fetch 返回的 json 数据

看完例子过后,就要动真格了,下面就来扒下 Fetch。

Promise特性

fetch方法返回一个 Promise 对象,根据 Promise Api 的特性,fetch可以方便地使用 then 方法将各个处理逻辑串起来,使用 Promise.resolve()Promise.reject() 方法将分别返会 肯定结果的Promise否定结果的Promise,从而调用下一个 then 或者 catch;一但 then 中的语句出现错误,也将跳到 catch 中。

若对Promise有疑问, 请阅读 Promises官方文档

情景1:同域下请求

我们在 https://sp0.baidu.com 域名的网页控制台运行以下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var word = '123',
url = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+word+'&json=1&p=3';

fetch(url).then(function(response){
console.log('第一次进入then...');
if(response.status >= 200 && response.status < 300){
console.log('Content-Type: ' + response.headers.get('Content-Type'));
console.log('Date: ' + response.headers.get('Date'));
console.log('status: ' + response.status);
console.log('statusText: ' + response.statusText);
console.log('type: ' + response.type);
console.log('url: ' + response.url);
return Promise.resolve(response);
} else {
return Promise.reject(new Error(response.statusText));
}
}).then(function(data){
console.log('第二次进入then...');
console.log(data);
}).catch(function(e){
console.log('抛出的错误如下:');
console.log(e);
});

运行截图如下:

Fetch 运行结果

情景2:非同域请求(跨域请求)

我们在非 https://sp0.baidu.com 域名的网页控制台再次运行以上代码(别忘了给fetch的第二参数传递 {mode: "no-cors"})。

运行截图如下:

Fetch 第二次运行结果

由于第一次进入 then 分支后,返回了否定结果的 Promise.reject 对象。因此代码进入到 catch 分支,抛出了错误。此时,上述 response.typeopaque

Response Type

一个fetch请求的响应类型(response.type)为如下三种之一:

  • basic
  • cors
  • opaque

如上情景1,同域下,响应类型为 “basic”;

如上情景2中,跨域下,服务器没有返回 CORS 响应头,响应类型为 “opaque”。此时我们几乎不能查看任何有价值的信息,比如不能查看response,status,url等等。

Fetch Response Type

同样是跨域下,如果服务器返回了CORS响应头,那么响应类型将为 “cors”。此时响应头中除 Cache-Control ,Content-Language ,Content-Type ,Expores ,Last-Modified 和 Progma 之外的字段都不可见。

注意:无论是同域还是跨域,以上 fetch 请求都到达了服务器。

mode

fetch可以设置不同的模式使得请求有效;模式可在fetch方法的第二个参数对象中定义。

1
fetch(url, {mode: 'cors'});

可定义的模式如下:

  • same-origin: 表示同域下可请求成功;反之,浏览器将拒绝发送本次fetch,同时抛出错误 “TypeError: Failed to fetch(…)”
  • cors: 表示同域和带有CORS响应头的跨域下可请求成功,其他请求将被拒绝
  • cors-with-forced-preflight: 表示在发出请求前,将执行preflight检查
  • no-cors: 常用于跨域请求不带CORS响应头场景,此时响应类型为 “opaque”

除此之外, 还有两种不太常用的mode类型,分别是 navigate,websocket,它们是 HTML标准 中特殊的值,这里不做详细介绍。

fetch 获取 HTTP 响应头非常容易,如下所示:

1
2
3
fetch(url).then(function(response) {
console.log(response.headers.get('Content-Type'));
});

设置 HTTP 请求头也一样简单:

1
2
3
4
5
var headers = new Headers();
headers.append("Content-Type", "text/html");
fetch(url, {
headers: headers
});

Header 的内容也可以被检索的:

1
2
3
4
5
6
7
8
var headers = new Headers({
"Content-Type": "text/plain"
});

// return true
console.log(header.has("Content-Type"));
// return false
console.log(header.has("Content-Length"));

post

在fetch中发送post请求,同样可以在fetch方法的第二个参数对象中设置。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
var headers = new Headers();
headers.append("Content-Type": "application/json;charset=UTF-8");

fetch(url, {
method: 'post',
headers: headers,
body: JSON.stringify(
date: '2017-05-08',
time: '10:56:19'
)
});

credentials

跨域请求中需要带有cookie时,可在fetch方法的第二个参数对象中添加 credentials 属性,并将值设置为 include

1
2
3
fetch(url, {
credentials: 'include'
});

除此之外,credentials 还可以取以下值:

  • omit: 缺省值,默认为该值
  • same-origin: 同源,表示同域请求才发送cookie

catch

同 XMLHttpRequest 一样,无论服务器返回什么样的状态码(chrome中除407之外的其他状态码),它们都不会进入到错误捕获里。也就是说,此时,XMLHttpRequest 实例不会触发 onerror 事件回调,fetch 不会触发 reject。通常只在网络出现问题时或者 ERR_CONNECTION_RESET 时,它们才会进入到相应的错误捕获里(其中,请求返回状态码为407时,chrome浏览器会触发 onerror 或者 reject 掉 fetch)。

cache

cache 表示如何处理缓存,遵守 HTTP 规范,拥有如下几种值:

  • 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”时有效)

如果fetch请求的header里包含 If-Modified-SinceIf-None-MatchIf-Unmodified-SinceIf-Match,或者 If-Range 之一,且cache的值为 default,那么fetch将自动把 cache的值设置为 no-store

【未完】