?fetch 的浏览器兼容
1 fetch 默认不携带 cookie
1fetch 发送请求默认是不发送 cookie 的, 不管是同域还是跨域;
需要设置 :
fetch(url, {credentials: 'include'})
可以配置其 credentials 项, 其有 3 个值:
1omit: 默认值, 忽略 cookie 的发送
2same-origin: 表示 cookie 只能同域发送, 不能跨域发送
3include: cookie 既可以同域发送, 也可以跨域发送
2fetch 默认对服务端通过 Set-Cookie 头设置的 cookie 也会忽略, 若想选择接受来自服务端的 cookie 信息, 也必须要配置 credentials 选项;
2 fetch 请求对某些错误 http 状态不会 reject
1这主要是由 fetch 返回 promise 导致的, 因为 fetch 返回的 promise 在某些错误的 http 状态下如 400,500 等不会 reject, 相反它会被 resolve;
2只有网络错误会导致请求不能完成时, fetch 才会被 reject; 所以一般会对 fetch 请求做一层封装
3示例:
- function checkStatus(response) {
- if (response.status>= 200 && response.status <300) {
- return response;
- }
- const error = new Error(response.statusText);
- error.response = response;
- throw error;
- }
- function parseJSON(response) {
- return response.JSON();
- }
- export default function request(url, options) {
- let opt = options||{};
- return fetch(url, {credentials: 'include', ...opt})
- .then(checkStatus)
- .then(parseJSON)
- .then((data) => ( data ))
- .catch((err) => ( err ));
3 fetch 不支持超时 timeout 处理
1fetch 不像大多数 Ajax 库那样对请求设置超时 timeout, 它没有有关请求超时的 feature, 所以在 fetch 标准添加超时 feature 之前, 都需要 polyfill 该特性.
2实际上, 我们真正需要的是 abort(), timeout 可以通过 timeout+abort 方式来实现, 起到真正超时丢弃当前的请求.
3而在目前的 fetch 指导规范中, fetch 并不是一个具体实例, 而只是一个方法;
4其返回的 promise 实例根据 Promise 指导规范标准是不能 abort 的, 也不能手动改变 promise 实例的状态, 只能由内部来根据请求结果来改变 promise 的状态.
5因此:
实现 fetch 的 timeout 功能, 其思想就是新创建一个可以手动控制 promise 状态的实例,
根据不同情况来对新 promise 实例进行 resolve 或者 reject, 从而达到实现 timeout 的功能;
6目前可以有两种不同的解决方法:
1第一种: 单纯 setTimeout 方式
- var oldFetchfn = fetch; // 拦截原始的 fetch 方法
- Windows.fetch = function(input, opts){// 定义新的 fetch 方法, 封装原有的 fetch 方法
- return new Promise(function(resolve, reject){
- var timeoutId = setTimeout(function(){
- reject(new Error("fetch timeout"))
- }, opts.timeout);
- oldFetchfn(input, opts).then(
- res=>{
- clearTimeout(timeoutId);
- resolve(res)
- },
- err=>{
- clearTimeout(timeoutId);
- reject(err)
- }
- )
- })
- }
在上面基础上可以模拟类似 XHR 的 abort 功能:
- var oldFetchfn = fetch;
- Windows.fetch = function(input, opts){
- return new Promise(function(resolve, reject){
- var abort_promise = function(){
- reject(new Error("fetch abort"))
- };
- var p = oldFetchfn(input, opts).then(resolve, reject);
- p.abort = abort_promise;
- return p;
- })
- }
2第二种方法: 利用 Promise.race 方法
Promise.race 方法接受一个 promise 实例数组参数, 表示多个 promise 实例中任何一个最先改变状态, 那么 race 方法返回的 promise 实例状态就跟着改变.
- var oldFetchfn = fetch; // 拦截原始的 fetch 方法
- Windows.fetch = function(input, opts){// 定义新的 fetch 方法, 封装原有的 fetch 方法
- var fetchPromise = oldFetchfn(input, opts);
- var timeoutPromise = new Promise(function(resolve, reject){
- setTimeout(()=>{
- reject(new Error("fetch timeout"))
- }, opts.timeout)
- });
- retrun Promise.race([fetchPromise, timeoutPromise])
- }
7timeout 不是请求连接超时的含义, 它表示请求的 response 时间, 包括请求的连接, 服务器处理及服务器响应回来的时间;
8fetch 的 timeout 即使超时发生了, 本次请求也不会被 abort 丢弃掉, 它在后台仍然会发送到服务器端, 只是本次请求的响应内容被丢弃而已;
4 fetch 不支持 JSONP
1fetch 是与服务器端进行异步交互的, 而 JSONP 是外链一个 JavaScript 资源, 并不是真正 Ajax, 所以 fetch 与 JSONP 没有什么直接关联, 当然至少目前是不支持 JSONP 的.
2我们要实现一个 JSONP, 只不过这个 JSONP 的实现要与 fetch 的实现类似, 即基于 Promise 来实现一个 JSONP; 而其外在表现给人感觉是 fetch 支持 JSONP 一样;
3JSONP 的实现步骤如下:
1首先需要用 NPM 安装 fetch-JSONP
NPM install fetch-JSONP --save-dev
2然后在像下面一样使用:
- fetchJsonp('/users.jsonp', {
- timeout: 3000,
- jsonpCallback: 'custom_callback'
- })
- .then(function(response) {
- return response.JSON()
- }).catch(function(ex) {
- console.log('parsing failed', ex)
- })
5 fetch 不支持 progress 事件
1XHR 是原生支持 progress 事件的
2但是呢, 根据 fetch 的指导规范标准, 其内部设计实现了 Request 和 Response 类;
其中 Response 封装一些方法和属性, 通过 Response 实例可以访问这些方法和属性:
3例如 response.JSON(),response.body 等等;
4response.body 是一个可读字节流对象, 其实现了一个 getRender() 方法, 其具体作用是:
getRender() 方法用于读取响应的原始字节流, 该字节流是可以循环读取的, 直至 body 内容传输完成;
6 fetch 跨域问题
1XHR2 是支持跨域请求的, 只不过要满足浏览器端支持 CORS, 服务器通过 Access-Control-Allow-Origin 来允许指定的源进行跨域, 仅此一种方式.
2fetch 也是支持跨域请求的, 只不过其跨域请求做法与 XHR2 一样, 需要客户端与服务端支持;
3fetch 还支持一种跨域, 不需要服务器支持的形式, 具体可以通过其 mode 的配置项来说明.
4fetch 的 mode 配置项有 3 个值:
1same-origin: 该模式是不允许跨域的, 它需要遵守同源策略, 否则浏览器会返回一个 error 告知不能跨域; 其对应的 response type 为 basic.
2cors: 该模式支持跨域请求, 顾名思义它是以 CORS 的形式跨域; 当然该模式也可以同域请求不需要后端额外的 CORS 支持; 其对应的 response type 为 cors.
3no-cors: 该模式用于跨域请求但是服务器不带 CORS 响应头, 也就是服务端不支持 CORS; 这也是 fetch 的特殊跨域请求方式; 其对应的 response type 为 opaque.
5针对跨域请求, cors 模式是常见跨域请求实现, 但是 fetch 自带的 no-cors 跨域请求模式则较为陌生, 该模式有一个比较明显的特点:
该模式允许浏览器发送本次跨域请求, 但是不能访问响应返回的内容, 这也是其 response type 为 opaque 透明的原因.
6这与 < img/> 发送的请求类似, 只是该模式不能访问响应的内容信息; 但是它可以被其他 APIs 进行处理, 例如 ServiceWorker.
7另外, 该模式返回的 repsonse 可以在 Cache API 中被存储起来以便后续的对它的使用, 这点对 script,CSS 和图片的 CDN 资源是非常合适的,
因为这些资源响应头中都没有 CORS 头.
总的来说, fetch 的跨域请求是使用 CORS 方式, 需要浏览器和服务端的支持.
7不可以取消
1.fetch 不支持 abort, 不支持超时控制, 使用 setTimeout 及 Promise.reject 的实现的超时控制并不能阻止请求过程继续在后台运行, 造成了流量的浪费.
2.fetch 没有办法原生监测请求的进度, 而 XHR 可以
参考: https://www.cnblogs.com/huilixieqi/p/6494380.html
来源: http://www.bubuko.com/infodetail-3356082.html