Abstract摘要:
1、Fetch的官方规范文档:https://fetch.spec.whatwg.org/,你可以去官网规范上查看如何使用fetch。
2、Fetch API 提供了一个 JavaScript 接口,优化了http请求和响应的操作方式。这种功能以前是使用 XMLHttpRequest
(ajax)实现的。它还提供了一个全局 fetch()
方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。
3、fetch最大的特点是与Promise对象的结合,Fetch API大多方法都返回一个Promise对象,使我们的编程风格更加优雅(上流)。我们知道jquery的ajax也实现了Promise功能;还有axios插件,Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
4、学习Fetch我们要搞懂几个知识点:fetch方法、Header对象、Request对象、Response对象等。
fetch的缺点:
兼容性不好的中止请求方法和没有实现的超时处理:无法像XMLHttpRequest那样abort中止请求,以及超时处理timeout。
兼容性:不支持IE,Edge14,Chrome40,Firefox39,Safari10。
Usage使用:
fetch()
方法用于发起获取资源的请求。它返回一个 promise,这个 promise 会在请求响应后被 resolve,并传回 Response
对象。
语法:
fetch(input[, init]);
input:定义要获取的资源,可以是一个url或者一个Request对象
init:可选的,一个配置对象,包含了对请求的设置
下面实例中,是对上述fetch()方法的最好解释。
var url = "https://mock.yonyoucloud.com/mock/16388/test/cities";
fetch(url, {
method: "GET",
mode: 'cors',
cache: 'default'
}).then(function(response){
return response.json(); //响应内容json化
}).then(function(res){
console.log(res); //打印响应值
})
fetch方法发起请求,返回一个promise对象,promise对象调用then方法,返回一个Response对象。注意这里返回的是Response对象。
必须调用两次then方法:Response对象的json()方法是对数据的处理,也是一个promise对象。第二个then方法的结果res,正是response.json()该promise对象的处理的结果。
我们还需要了解别的响应数据的格式:
1、Response.json()会将响应流数据读成json格式:
2、而Response.text()会将响应流数据读成text类型:
var url = "https://mock.yonyoucloud.com/mock/16388/test/cities";
var cityRequest = new Request(url);
fetch(cityRequest).then(function (response) {
response.text().then(function (res) {
console.log(res);
});
})
{"success":true,"data":["北京","上海"]}
注意:
(1)上面代码的写法,我们说过,Response.json()、Response.blob()等都是Promise对象,所以response.text()可以直接调用then方法。
(2)fetch方法第一个参数也可以接收一个Request。
3、Response.blob()可以将响应流数据读成二进制格式。上述实例的blob()方法输出:
blob方法处理图片,比如处理图片资源,如下:
var myImage = document.querySelector('img');
var myRequest = new Request('https://dss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1819216937,2118754409&fm=26&gp=0.jpg');
fetch(myRequest).then(function (response) {
response.blob().then(function (myBlob) {
var objectURL = URL.createObjectURL(myBlob);
console.log(objectURL);
myImage.src = objectURL;
});
});
波及到的知识点,具体分析:
一、fetch的使用
参数1:请求资源;参数2:init配置项:可选参数,一个可以控制不同配置的 init
对象
以一个实例开始:POST请求的实现
postData('https://mock.yonyoucloud.com/mock/16388/test/post/star', {
name: "liu"
})
.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', //
credentials: 'same-origin', // *omit不带cookie, same-origin同源带cookie, include跨域带cookie
headers: {
'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
}
init的配置项可以参考:https://developer.mozilla.org/zh-CN/docs/Web/API/WindowOrWorkerGlobalScope/fetch
method
: 请求使用的方法,如GET、
POST。默认GET
headers
: 请求的头信息,形式为Headers
的对象或包含ByteString
值的对象字面量。比如:headers : { 'Content-Type' : 'application/json' },可以设置所有的header信息。body
: 请求的 body 信息:可能是一个Blob
、BufferSource
、FormData
、URLSearchParams
或者USVString
对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。mode
: 请求的模式,如 默认cors跨域、
no-cors不跨域 或者
same-origin。
credentials
: 请求的 credentials,如 默认omit不携带cookie、
same-origin同源携带cookie 或者
include跨域携带cookie
。但在Chrome中,Chrome 47 之前的版本默认值为same-origin
,自Chrome 47起,默认值为include。
cache
: 请求的 cache 模式:default
、no-store不缓存
、reload
、no-cache有缓存但仍需要去服务器获取资源
、force-cache
或者only-if-cached
。redirect
: 可用的 redirect 模式:follow
(自动重定向),error
(如果产生重定向将自动终止并且抛出一个错误), 或者manual
(手动处理重定向). 在Chrome中默认使用follow(
Chrome 47之前的默认值是manual
)。- referrer: 一个
USVString
可以是no-referrer、
client
或一个 URL。默认是client。
referrerPolicy
: 指定了HTTP头部referer字段的值。可能为以下值之一:no-referrer、
no-referrer-when-downgrade、
origin、
origin-when-cross-origin、
unsafe-url 。
拓展:结合async和await使用fetch:
async function asyncFetch (){
let response = await fetch('https://mock.yonyoucloud.com/mock/16388/test/cities');
let data = await response.json();
console.log(data);
}
asyncFetch()
拓展: 如何中止fetch请求呢?
一个实验中的api,具有abortI方法的AbortController()
构造函数。 兼容性,不太良好。IE不兼容,Edge16,Firefox57,Chrome66,Safari12。
以下是取消 fetch
调用的工作流程:
- 创建一个
AbortController
实例 - 该实例具有
signal
属性 - 将
signal
传递给 fetch option 的signal
- 调用
AbortController
的abort
属性来取消所有使用该信号的 fetch。
示例中,发起请求下载视频资源,下载的过程中,中止下载。
fetch方法中的配置项中,添加了新的的配置项,signal
var controller = new AbortController();
var signal = controller.signal;
var downloadBtn = document.querySelector('.download');
var 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) {
reports.textContent = 'Download error: ' + e.message;
})
}
中止请求:controller.abort()方法,
const controller = new AbortController();
const signal = controller.signal;
console.log(signal);
fetch('https://mock.yonyoucloud.com/mock/16388/test/cities', {
signal
}).then(function (response) {
response.json().then(function (res) {
console.log(res);
})
})
controller.abort();//终止请求,但是如何区分各个fetch请求呢?
在 abort
调用时发生 AbortError,上述代码结果如下:一个是signal对象,一个是中止请求成功后报的错误AbortError。
但是怎么区分每个fetch请求呢?每创建的一个controller对象,将controller的signal传入到fetch请求的配置项中,当执行controller.abort()方法时,就会去中止该controller的signal项。
实现一个通用方法:
function abortableFetch(request, opts = {}) {
const controller = new AbortController();
const signal = controller.signal;
opts.single = signal;
return {
abort: () => controller.abort(),
startFetch: () => fetch(request, opts)
};
}
var abortFetch = abortableFetch('https://mock.yonyoucloud.com/mock/16388/test/cities')
abortFetch.startFetch().then(function(response){
response.json().then(function(res){
console.log(res);
})
})
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/API/FetchController/abort
https://segmentfault.com/a/1190000022148239
https://davidwalsh.name/cancel-fetch
拓展:POST请求可以在body中设置参数,GET请求如何设置请求参数呢?
fetch方法的GET请求,不允许设置body。那该如何传参呢?
方法1:拼接url
var page = 1;
var name = "beijing";
var url = 'https://mock.yonyoucloud.com/mock/16388/test/cities';
var params = '?page=' + page + '&' + 'name=' + name;
url = url + params;
console.log(url); //https://mock.yonyoucloud.com/mock/16388/test/cities?page=1&name=beijing
fetch(url).then(response => response.json().then(res => {
console.log(res);
}))
方法2:通过该文最后所提到的,额外补充的知识,关于url 的操作,可以使用URLSearchParams构造函数,创建url的拼接参数。 URLSearchParams构造函数的兼容性:Edge17、Firefox29、Chrome49
(1)URLSearchParams构造函数的使用
可接受query字符串参数;也可以接受二维数组,像Map那样。得到的Params对象,调用toString方法,得到query string parameters
// 创建url对象
var url = new URL('https://mock.yonyoucloud.com/mock/16388/test/cities?page=1&name=beijing');
var params = new URLSearchParams(url.search); //url.search是查询字符的的内容,"?page=1&name=beijing"
// 接收查询字符串的内容
var params2 = new URLSearchParams("foo=1&bar=2");
var params2a = new URLSearchParams("?foo=1&bar=2");
// 接收一个二维数据
var params3 = new URLSearchParams([
["foo", "1"],
["bar", "2"]
]);
// 接收一个json对象
var params4 = new URLSearchParams({
"foo": "1",
"bar": "2"
});
console.log(params.toString()); //page=1&name=beijing
console.log(params2.toString()); //foo=1&bar=2
console.log(params2a.toString()); //foo=1&bar=2
console.log(params3.toString());// foo=1&bar=2
console.log(params4.toString()); //foo=1&bar=2
(2)所以可以这么用:
var page = 1, name = 'beijing';
// 接收一个json对象
var params = new URLSearchParams({
"page": page,
"name": name
});
url = url + '?' + params.toString();
console.log(url); //https://mock.yonyoucloud.com/mock/16388/test/cities?page=1&name=beijing
二、Request对象
我们可以通过Request构造函数, 构造一个Request对象,而fetch的第一个参数可以接受Request对象。
语法:var myRequest = new Request(input[, init]);
参数:
input
定义你想要fetch的资源。可以是下面两者之一:
- 一个直接包含你希望 fetch 的资源的 URL 的
USVString
。- 一个
Request
对象。请注意以下行为更新,以在保留安全性的同时使构造函数不太可能引发异常:
- 如果此对象存在于构造函数调用的另一个起源上,则将除去
Request.referrer
。- 如果此对象的导航为
Request.mode
,则mode
将转换为same-origin
。init 可选
一个可选对象,包含希望被包括到请求中的各种自定义选项。可用的选项如下:
method
: 请求的方法,例如:GET
,POST。
headers
: 任何你想加到请求中的头,其被放在Headers
对象或内部值为ByteString
的对象字面量中。body
: 任何你想加到请求中的body,可以是Blob
,BufferSource
,FormData
,URLSearchParams
, 或USVString
对象。注意GET
和HEAD请求没有body。
mode
: 请求的模式, 比如cors
,no-cors
,same-origin
, 或navigate
。默认值为cors
。credentials
: 想要在请求中使用的credentials::omit
,same-origin
, 或include
。默认值应该为omit
。(自 2017 年 8 月 25 日以后,默认的 credentials 政策变更为same-origin
。Firefox 也在 61.0b13 版本中进行了修改)。但在Chrome中,Chrome 47 之前的版本默认值为same-origin
,自Chrome 47起,默认值为include。
cache
: 请求中想要使用的 cache moderedirect
: 对重定向处理的模式:follow
,error
, ormanual
。在Chrome中,Chrome 47 之前的版本默认值为manual
,自Chrome 47起,默认值为follow。
referrer
: 一个指定了no-referrer
,client
, 或一个 URL的USVString
。默认值是about:client
。integrity
: 包括请求的 subresource integrity 值 (e.g.,sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=
).
var myRequest = new Request('https://mock.yonyoucloud.com/mock/16388/test/cities',{
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
mode: 'cors'
})
fetch(myRequest).then(function(response){
response.json().then(function(res){
console.log(res);
})
})
三、Response对象
当fetch()方法执行结束时,会返回一个Response对象,我们可以通过Response对象的属性和方法来查看相关响应信息。Response对象的方法来处理response响应,比如json()、text()、blob()等方法。
Response.headers 值 对象, 只读 ,包含该Response所关联的Headers对象
Response.ok 值true ,只读,包含了一个布尔值,表示该Response是否成功(状态码在200-299的为true)
Response.status 值200 ,只读,该Response状态码,200表示成功。404表示服务器没有该资源。
Response.statusText 值OK, 只读,表示与status状态码一直的信息,200 对应 OK
Response.url 值是一个url地址,只读,表示该Response的url
Response.redirected 只读,表示该 Response 是否来自一个重定向,如果是的话,它的 URL 列表将会有多个条目。
Response.type 响应类型,只读,包含 Response 的类型(例如,
basic
、cors
)。Response.body 一个简单的 getter,表示 body 内容。
几个方法:Response实现了Body接口,所以可以使用以下方法:
Body.json()
读取Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为JSON
格式的 Promise 对象。
Body.text()
读取Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为USVString
格式(文本格式)的 Promise 对象。
Body.blob()
读取Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为Blob
格式(二进制格式,包含二进制数据信息)的 Promise 对象。
Body.formData()
读取Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为FormData
格式的 Promise 对象。
Body.arrayBuffer()
读取Response
对象并且将它设置为已读(因为 Responses 对象被设置为了 stream 的方式,所以它们只能被读取一次),并返回一个被解析为ArrayBuffer
格式的 Promise 对象。
四、Headers对象
Headers 接口允许您对HTTP请求和响应头执行各种操作。 这些操作包括检索,设置,添加和删除。
一个Headers对象具有关联的头列表,它最初为空,由零个或多个键值对组成。
在该接口的所有方法中,标题名称由不区分大小写的字节序列匹配。
出于安全考虑,某些头只能由用户代理控制。这些头信息包括 forbidden header names 和 forbidden response header names。
————简单的说:Headers对象就是设置请求头headers的。
Headers对象的方法:
Headers.append()方法:给现有的header添加值。
Headers.delete()方法,删除现有header的某个值。
Headers.get()方法,获取现有header的某个值。
Headers.has()方法,判断现有header中是否有某个值。
使用:用于fetch方法的配置项中的headers
let myHeaders = new Headers(); //创建一个header空对象。
myHeaders.append('Content-Type', 'text/xml');
console.log(myHeaders.get('Content-Type')); // should return 'text/xml
console.log(myHeaders.has('content-type')); //true ,不区分大小写
fetch('https://mock.yonyoucloud.com/mock/16388/test/cities', {
headers: myHeaders
}).then(response => response.json().then(res => {
console.log(res);
}))
比如获取图片:
var myImage = document.querySelector('img');
var myHeaders = new Headers();
myHeaders.append('Content-Type', 'image/jpeg');
var myInit = { method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default' };
var myRequest = new Request('flowers.jpg');
fetch(myRequest,myInit).then(function(response) {
...
});
五、Blob对象
Blob对象代表响应/请求的正文,允许你声明其内容类型是什么以及应该如何处理。Body
被Request和Response实现(Request中的body、Response中的body),并为这些对象提供了一个相关联的主体(字节流),一个已使用的标志(最初未设置)和一个MIME类型(最初为空字节序列)。
Body.body属性(被Request和Response实现了该属性),一个简单的getter,用于获取主体内容。
Body的方法,如上所述。
六、fetch、jquery的ajax、axios的区别
fetch
规范与 jQuery.ajax()
主要有三种方式的不同:
- 当接收到一个代表错误的 HTTP 状态码时,从
fetch()
返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的ok
属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。——404 或500 状态码的处理,fetch会返回resolve,而jquery.ajax返回reject。 - 关于cookie,规范由默认不带,该为默认同源带cookie;Chrome由默认同源带cookie,改为跨域也带cookie。
1、原生ajax的实现:
五步骤:
1)创建XmlHttpRequest对象
2)调用open方法设置基本请求信息
3)设置发送的数据,发送请求
4)注册监听的回调函数
5)拿到返回值,对页面进行更新
//1.创建Ajax对象
if(window.XMLHttpRequest){
var oAjax=new XMLHttpRequest();
}else{
var oAjax=new ActiveXObject("Microsoft.XMLHTTP");
}
//2.连接服务器(打开和服务器的连接)
oAjax.open('GET', url, true);
//3.发送
oAjax.send();
//4.接收
oAjax.onreadystatechange=function (){
if(oAjax.readyState==4){
if(oAjax.status==200){
//alert('成功了:'+oAjax.responseText);
fnSucc(oAjax.responseText);
}else{
//alert('失败了');
if(fnFaild){
fnFaild();
}
}
}
};
在ES6的基础上,通过XMLHttpRequest对象和Promise、async、await来实现一个fetch:
参考文章:https://www.jianshu.com/p/7762515f8d1a
2、$.ajax()的使用:
$.ajax({
url: "https://mock.yonyoucloud.com/mock/16388/test/cities",
type: 'GET',
data: {
},
success: function(res){
console.log(res);
},
timeout: 5000, //超时设置
dataType: 'json', //预期服务器响应的内容类型。
cache: true, //默认值true,缓存
contentType: 'application/x-www-form-urlencoded', //默认值 发送数据到服务器时所使用的内容类型。
beforeSend: function(){ //发送前运行的函数
}
})
jquery中ajax的promise使用:
$.get('https://mock.yonyoucloud.com/mock/16388/test/cities').then(function(res){
console.log(res);
return $.get('https://mock.yonyoucloud.com/mock/16388/test/movies')
}).then(function(res){
console.log(res);
}).catch(function(err){
console.log(err);
})
3、axios的使用
axios官网:http://www.axios-js.com/
具体的使用还需要深入研究。。。
4、GitHub上的fetchpollfill
地址:https://github.com/github/fetch
该pollfill的支持情况:Browser Support:
- Chrome
- Firefox
- Safari 6.1+
- Internet Explorer 10+
具体的使用,还需要深入研究。。。
额外,关于url的操作:
1、URL.createObjectURL() 创建一个url
URL.createObjectURL()
静态方法会创建一个 DOMString
,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document
绑定。这个新的URL 对象表示指定的 File
对象或 Blob
对象。
语法:objectURL = URL.createObjectURL(object);
参数object:用于创建 URL 的
File
对象、Blob
对象或者MediaSource
对象。
上面例子中,使用的Blob对象。
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL
2、URLSearchParams.toString
接口的toString()
方法URLSearchParams
返回适合在URL中使用的查询字符串。——此方法返回不带问号的查询字符串
语法: URLSearchParams.toString()
URL构造函数:兼容性:ES6,Edge12
let url = new URL('https://example.com?foo=1&bar=2');
console.log(url);
查看结果:拥有所有关于url的信息。
关于url的参数的一些操作:
let url = new URL('https://example.com?foo=1&bar=2');
console.log(url);
let params = new URLSearchParams(url.search.slice(1)); //url.search = ?foo=1&bar=2
console.log(params); //URLSearchParams对象
//Add a second foo parameter.
params.append('foo', 4); //URLSearchParams对象的append方法,添加新的键值对。
console.log(params.toString()); //取值:Prints 'foo=1&bar=2&foo=4'
参考文章:https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
https://developer.mozilla.org/zh-CN/docs/Web/API/WindowOrWorkerGlobalScope/fetch