什么是fetch
fetch、ajax、axios有什么区别
ajax
- 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
- 基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
- JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
axios
- 从浏览器中创建XMLHttpRequest
- 从node.js 中发出http请求
- 支持 Promise API
- 客户端支持防止CSRF(伪脚本攻击)
- 提供了并发请求API接口
- 支持请求/响应拦截
- 取消请求
- 自动转换json数据
fetch
- 基于promise,关注分离,没有将输入、输出和用事件来跟踪的状态混合在一个对象中
- 更加底层化,提供了丰富的API(request, response)
- 脱离了XHR,是ES规范里新的实现方式
- fetchtch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
- fetch默认不会带cookie,需要添加配置项
- fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
- fetch没有办法原生监测请求的进度,而XHR可以
fetch用法
response
|
|
从上面例子看出请求是成功的(ok是true,status是200),但是我们想获取的仓库名却不在这里。
请求的资源都存储在body中,作为一种可读的流。所以需要调用一个恰当方法将可读流转换为我们可以使用的数据。
- JSON格式的,使用response.json方法来转换数据。
- 一个XML格式文件,则调用response.text。
- 图片,使用response.blob方法。
所有这些方法(response.json等等)返回另一个Promise,所以可以调用.then方法处理我们转换后的数据。
处理异常
虽然希望Ajax响应成功,但是仍会有问题出现:
- 可能尝试获取不存在的资源
- 没有权限获取资源
- 输入参数有误
- 服务器抛出异常
- 服务器超时
- 服务器崩溃
- API更改
- …
fetch请求进入then方法,仅代表请求发送成功和接收请求,但是不能是否相应失败,所以需要手动判断并处理异常
但对于下面这些特定的错误不适用:
400:Bad request
例如,如果请求错误缺少必要的参数,就会返回400.
光在catch中告诉状态及原因短语并不足够。我们需要知道缺少什么参数。
所以服务器需要返回一个对象,告诉造成错误请求原因。如果使用Node和Express,会返回像下面这样的响应:
无法在最初的.then方法中reject,因为错误对象需要response.json来解析。
解决的方法是需要两个then方法。这样可以首先通过response.json读取,然后决定怎么处理。
首先我们调用response.json读取服务器发来的JSON数据,response.json返回Promise,所以可以链式调用.then方法。
在第一个.then中调用第二个.then,因为我们仍希望通过repsonse.ok判断响应是否成功。
如果想发送状态和原因短语,可以使用Object.assign()将二者结合为一个对象。
可以使用这样新的handleResponse函数,让数据能自动的进入.then和.catch中。
fetch使用的常见问题及解决办法
fetch默认不携带cookie
fetch请求对某些错误http状态不会reject(400,500)
|
|
fetch不支持超时timeout处理
用过fetch的都知道,fetch不像大多数ajax库那样对请求设置超时timeout,它没有有关请求超时的feature,这一点比较蛋疼。所以在fetch标准添加超时feature之前,都需要polyfill该特性。
实际上,我们真正需要的是abort(), timeout可以通过timeout+abort方式来实现,起到真正超时丢弃当前的请求。
方法一:单纯setTimeout方式
方法二:利用Promise.race方法
Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变,具体可以参考这里。
【注意】
- timeout不是请求连接超时的含义,它表示请求的response时间,包括请求的连接、服务器处理及服务器响应回来的时间;
- fetch的timeout即使超时发生了,本次请求也不会被abort丢弃掉,它在后台仍然会发送到服务器端,只是本次请求的响应内容被丢弃而已;
fetch不支持JSONP
fetch不支持progress事件
XHR是原生支持progress事件的,但fetch是不支持有关progress事件的;不过可喜的是,根据fetch的指导规范标准,其内部设计实现了Request和Response类;其中Response封装一些方法和属性,通过Response实例可以访问这些方法和属性,例如response.json()、response.body等等;
fetch跨域问题
既然是ajax库,就不可避免与跨域扯上关系;XHR2是支持跨域请求的,只不过要满足浏览器端支持CORS,服务器通过Access-Control-Allow-Origin来允许指定的源进行跨域,仅此一种方式。
与XHR2一样,fetch也是支持跨域请求的,只不过其跨域请求做法与XHR2一样,需要客户端与服务端支持;另外,fetch还支持一种跨域,不需要服务器支持的形式,具体可以通过其mode的配置项来说明。
fetch的mode配置项有3个值,如下:
same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic。
cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors。
no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque。
针对跨域请求,cors模式是常见跨域请求实现,但是fetch自带的no-cors跨域请求模式则较为陌生,该模式有一个比较明显的特点:
该模式允许浏览器发送本次跨域请求,但是不能访问响应返回的内容,这也是其response type为opaque透明的原因。
这与发送的请求类似,只是该模式不能访问响应的内容信息;但是它可以被其他APIs进行处理,例如ServiceWorker。另外,该模式返回的repsonse可以在Cache API中被存储起来以便后续的对它的使用,这点对script、css和图片的CDN资源是非常合适的,因为这些资源响应头中都没有CORS头。