fetch请求和ajax请求 fetch与ajax(XMLHttpRequest)相比
ES6中新增了一种HTTP数据请求的方式,就是fetch,它和XMLHttpRequest有许多相似的功能,但是相比XMLHttpRequest,fetch被设计成更具可扩展性和高效性。江湖上一直流传着 “传统ajax已死,fetch永生”的说法,下面详细说下二者
1 | response.arrayBuffer() |
- 语法简洁,更加语义化
- 基于标准 Promise 实现,支持 async/await
- 同构方便,使用 isomorphic-fetch
- fetch 一直没有传统的 XHR 所拥有的 abort() ,setTimeout超时回调 和 onprogress 功能
1
2
3
4
5
6
7
8
9fetch(url,{
method:'POST', // method:'PUT', method:'DELETE',
headers:{
'Content-type':'application/json'// 设置请求头数据类型
},
body:data
})
.then(res=>res.json())
.then(data=>console.log(data))
fetch可以在header中设置CORS跨域
1 | var u = new URLSearchParams(); |
ajax
- 1.是XMLHTTPRequest的一个实例,需要new
- 2.只有当状态为200或者304时才会请求成功
- 3.格式零散,容易出现回调地狱的问题
fetch
- 1.fetch是基于promise实现的,也可以结合async/await
- 2.fetch请求默认是不带cookie的,需要设置fetch(URL,{credentials:’include’})。
- Credentials有三种参数:same-origin,include,*
- 3.服务器返回400 500 状态码时并不会reject,只有网络出错导致请求不能完成时,fetch才会被reject
- 4.所有版本的 IE 均不支持原生 Fetch
- 5.fetch是widow的一个方法,不需要new
- 6.fetch 一直没有传统的 XHR 所拥有的 abort() ,setTimeout超时回调 和 onprogress 功能
fetch和ajax 的主要区别
- 1、fetch()返回的promise将不会拒绝http的错误状态,即使响应是一个HTTP 404或者500
- 2、在默认情况下 fetch不会接受或者发送cookies
- 3、所有的IE浏览器都不会支持 fetch()方法
- 4、服务器端返回 状态码 400 500的时候 不会reject
1 | fetch(url).then(function (response) { |
fetch 的第二参数中
1、默认的请求为get请求 可以使用method:post 来进行配置
2、第一步中的 response有许多方法 json() text() formData()
3、Fetch跨域的时候默认不会带cookie 需要手动的指定 credentials:’include’1
2
3
4
5
6
7
8
9
10
11
12var promise=fetch('http://localhost:3000/news', {
method: 'get'
}).then(function(response) {
return response.json()
}).catch(function(err) {
// Error :(
});
promise.then(function (data) {
console.log(data)
}).catch(function (error) {
console.log(error)
})
fetch模拟abort请求 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
超时设置
原生的XMLHttpRequest可以设置请求超时时间的方法回调;
ES6以后Promise 出现解决地狱回调等不优雅的代码风格。个人理解这个更像是一个生产者和消费者的关系,查看 Promise文档,有以下两个方法
- Promise.race([promise1,promise2]) 传入多个Promise对象,等待最快对象完成
- Promise.all([promise1,promise2]) 传入多个Promise 对象,等待所有对象完成
有了以上知识后,结合函数setTimeout就可以实现超时设置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//ahutor:herbert qq:464884492
let timeoutPromise = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("我是 timeoutPromise,已经完成了");
}, timeout);
});
}
let requestPromise = (url) => {
return fetch(url);
};
Promise.race([timeoutPromise(1000), requestPromise("https://www.baidu.com")])
.then(resp => {
console.log(resp);
})
.catch(error => {
console.log(error);
});
Ajax与Comet
可以检测XHR对象的readyState属性,该属性表示请求/响应过程的当前活动阶段
- 0:未初始化。尚未调用open()方法
- 1:启动。已经调用open()方法,但尚未调用send()方法
- 2:发送。已经调用send()方法,但尚未接收到响应
- 3:接收。已经接收到部分响应数据
- 4:完成。已经接收全部响应数据,而且已经可以在客户端使用了。
1 | function createXHR() { |
Ajax
1 | // 获取异步请求对象 |
HTTP缓存机制(200还是304)
Expires Cache-Control
- Cache-Control: no-cache 必须先与代理服务器确认是否更改,然后在在决定使用缓存还是请求,类似于协商缓存(304)
- Cache-Control: no-store 才是真正的不缓存数据到本地
- Cache-Control: public 可以被所有用户缓存(多用户共享),包括终端和CDN等中间代理服务器
- Cache-Control: private 只能被终端浏览器缓存(而且是私有缓存),不允许中继缓存服务器进行缓存
- Cache-Control: must-revalidate如果缓存内容失效,请求必须发送服务器进行验证
- Cache-Control: max-age=s 缓存内容在s秒后失效,仅HTTP1.1可用
HTTP缓存机制及原理
1 | Expires |
BFC是什么
什么是BFC?
- 根元素或其它包含它的元素
- 浮动元素 (元素的 float 不是 none)
- 绝对定位元素 (元素具有 position 为 absolute 或 fixed)
- 内联块 (元素具有 display: inline-block)
- 表格单元格 (元素具有 display: table-cell,HTML表格单元格默认属性)
- 表格标题 (元素具有 display: table-caption, HTML表格标题默认属性)
- 具有overflow 且值不是 visible 的块元素,
- display: flow-root
- column-span: all 应当总是会创建一个新的格式化上下文,即便具有 column-span: all 的元素并不被包裹在一个多列容器中。
- 一个块格式化上下文包括创建它的元素内部所有内容,除了被包含于创建新的块级格式化上下文的后代元素内的元素。
1 | 哪些元素会生成BFC? |
BFC的布局规则 什么是BFC?看这一篇就够了
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
BFC是一个独立的布局环境,其中的元素布局是不受外界的影响,并且在一个BFC中,块盒与行盒(行盒由一行中所有的内联元素所组成)都会垂直的沿着其父元素的边框排列。
- 内部的Box会在垂直方向,一个接一个地放置。
- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。
每个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC的区域不会与float box重叠。
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- 计算BFC的高度时,浮动元素也参与计算。
如何创建BFC
1、float的值不是none。
2、position的值不是static或者relative。
3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
4、overflow的值不是visible
BFC的作用
1.利用BFC避免margin重叠。
2.自适应两栏布局
3.清楚浮动
🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
React HOC的两种实践
从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 && HOC 探幽
react高阶组件的理解
React进阶之高阶组件
- 属性代理
- 渲染劫持
- 反向继承
存在两种高阶组件:
- 属性代理—主要进行组件的复用
- 反向集成【渲染的劫持】—主要进行渲染的劫持
箭头函数和普通函数的区别 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
- 箭头函数是匿名函数,不能作为构造函数,不能使用new
- 箭头函数的 this 永远指向其上下文的 this ,任何方法都改变不了其指向,如 call() , bind() , apply()
- 箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
- 箭头函数不能绑定arguments,取而代之用rest参数…解决
- 箭头函数没有原型属性
- 头函数的this永远指向其上下文的this,没有办改变其指向,普通函数的this指向调用它的对象
- 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
- 箭头函数不能当做Generator函数,不能使用yield关键字
- 普通函数的this指向调用它的那个对象
fetch 的控制器和观察者 fetch 的控制器和观察者
因为受 cancelable promise 的拖延,fetch 一直没有传统的 XHR 所拥有的 abort() 和 onprogress 功能,去年年底 cancelable promise 草案被彻底废弃了,所以今年年初的这几个月里,一份完全新的、fetch 专用的、不依赖 JS 规范、与 Promise 没有任何直接关系的草案诞生了,而且 Firefox 已经率先实现了其中的大部分功能。
这份草案中引入了 3 种新的对象类型,FetchController(fetch 控制器)、FetchSignal(fetch 信号)、FetchObserver(fetch 观察者),虽然这三个都是全局构造函数,但其实只有 FetchController 对象是真的需要你手动 new 出来的,其它两种对象都是浏览器为你生成的,我们下面分别演示下如何中断(取消、中止)一个 fetch 请求,以及如何监听 fetch 请求的执行进度。
中断 fetch 请求
1 | const controller = new FetchController() |
这段代码中,我们先创建了一个 fetch 控制器 controller,每个控制器都自带一个 fetch 信号对象(signal 属性),然后在你用 fetch() 发送请求的时候把这个信号对象带上(signal 参数)。之后,你就可以通过控制器的 abort() 方法,来中断那个 fetch 请求了。只要在你执行 abort() 方法的时候,那个 fetch 请求还没有执行完毕(请求还没发出去、请求发出去了还没接收到响应、响应还没接收完),那么当初 fetch() 返回的那个 promise 就会被 reject,所产生的 error 对象的 name 为 AbortError。
上面这个例子中,我是在 500 毫秒的时候中断了这个请求,这是个绝对的数字,所以根据你的网速不同,执行代码时你有可能会看到弹出“请求成功”,也有能看到“请求被中断”。
一个信号对象还可以同时传递给多个 fetch 请求:1
2
3
4
5
6
7
8const controller = new FetchController()
const signal = controller.signal
fetch("https://tc39.github.io/ecma262/", { signal })
fetch("https://fetch.spec.whatwg.org/", { signal })
fetch("https://dom.spec.whatwg.org/", { signal })
controller.abort() // 同时中断三个请求
信号对象有一个 aborted 属性表明自己是否已经被中断过了,一旦被中断,它不可能回到当初的状态,也就是说,你不能二次利用一个已经被中断过的信号对象,把这样的信号对象传给 fetch() 的话,fetch() 会立即结束,不会发出请求:1
2
3
4
5
6
7
8const controller = new FetchController()
const signal = controller.signal
controller.abort()
alert(signal.aborted) // true
fetch("https://tc39.github.io/ecma262/", { signal }) // 直接被 reject,请求不会发出
信号对象上还可以监听 abort 事件,不过我想一般用不上,因为请求中断后的处理代码一般写在 fetch(…).catch(…) 里面:1
2
3
4
5
6
7
8
9
10
11const controller = new FetchController()
const signal = controller.signal
signal.addEventListener("abort", function(e) { // 使用 signal.onabort = 注册监听函数也可以
alert(e.type) // abort
alert(signal.aborted) // true
})
alert(signal.aborted) // false
controller.abort()
一个控制器除了可以让自己的信号对象 abort,还可以关注(跟随、监听)一个别的控制器的信号对象,只要关注的这个信号对象被 abort 了,自己的 abort() 方法就会被自动执行,从而自己的信号对象也会被 abort:
1 | const fc1 = new FetchController() |
再来看个更复杂的例子:1
2
3
4
5
6
7
8
9
10
11
12const fc1 = new FetchController()
const fc2 = new FetchController()
const fc3 = new FetchController()
fc1.follow(fc2.signal)
fc2.follow(fc3.signal)
fetch("https://tc39.github.io/ecma262/", { signal: fc1.signal })
fetch("https://fetch.spec.whatwg.org/", { signal: fc2.signal })
fetch("https://dom.spec.whatwg.org/", { signal: fc3.signal })
fc3.abort() // 三个请求都中断了
一个控制器只能关注一个别人家的信号对象,而一个信号对象可以被任意多个别家的控制器关注。不能直接或间接的关注自己的信号对象,比如上面的代码中再增加一行 fc3.follow(fc1.signal) 就会产生环形关注,所以那句代码会没有任何效果。还有一个 unfollow() 方法可以取消关注。
总结下就是,一个控制器对象有一个 signal 属性指向一个信号对象,还有 abort()、follow()、unfollow() 这三个方法。一个信号对象有一个 aborted 属性,还有一个 onabort 事件监听函数。
监听 fetch 请求
上面说到了 FetchController 和 FetchSignal,接下来要说说 fetch 观察者 - FetchObserver 对象。fetch 观察者对象上可以监听 statechange、requestprogress、responseprogress 三种事件,分别用来获取 fetch 请求的状态信息变化、请求数据发送的大小变化(POST 请求)、响应数据接收的大小变化信息,先来看看如何监听状态信息的变化:
1 | fetch("https://tc39.github.io/ecma262/", { |
FetchObserver 对象不用你手动 new,浏览器会为你 new 一个,在 fetch 请求开始之前,通过你事先指定的 observe 回调函数的参数传递给你。然后你在这个观察者对象上注册 statechange 监听函数,就可以在请求的不同阶段执行特定的代码了。
监听响应的接收进度使用 responseprogress 事件:
1 | fetch("https://img.alicdn.com/tfscom/TB1y6icQXXXXXaQXFXXXXXXXXXX.mp4", { |
监听 POST 数据的发送进度使用 requestprogress 事件:
1 | fetch("/upload", { |
在本文发布的现在,规范草案还只是最初的阶段,可能有很多细节没考虑,目前 Firefox 也没有实现我上面所说的所有功能,实现了的也有一些 bug,而且还需要事先在 about:config 手动添加某两个选项才可以使用,所以本文仅仅是简要介绍,大家暂时眼睛看看就够了。
以后 fetch 控制器的功能可能还会增加,不仅仅能让一个 fetch 请求中断,比如还可以指定这个请求的优先级,fetch 观察者同时也可以用来监听 fetch 请求优先级的变化。
fetch默认不携带cookie
fetch发送请求默认是不发送cookie的,不管是同域还是跨域;那么问题就来了,对于那些需要权限验证的请求就可能无法正常获取数据,这时可以配置其credentials项,其有3个值:
- omit: 默认值,忽略cookie的发送
- same-origin: 表示cookie只能同域发送,不能跨域发送
- include: cookie既可以同域发送,也可以跨域发送
credentials所表达的含义,其实与XHR2中的withCredentials属性类似,表示请求是否携带cookie;具体可以参考阮一峰老师的跨域资源共享 CORS 详解中withCredentials一节的介绍;
若要fetch请求携带cookie信息,只需设置一下credentials选项即可,例如
1 | fetch(url, {credentials: 'include'}) |
fetch请求对某些错误http状态不会reject
这主要是由fetch返回promise导致的,因为fetch返回的promise在某些错误的http状态下如400 、500等不会reject,相反它会被resolve;只有网络错误会导致请求不能完成时,fetch 才会被 reject;所以一般会对fetch请求做一层封装,例如下面代码所示:
1 | function checkStatus(response) { |
fetch不支持超时timeout处理
用过fetch的都知道,fetch不像大多数ajax库那样对请求设置超时timeout,它没有有关请求超时的feature,这一点比较蛋疼。所以在fetch标准添加超时feature之前,都需要polyfill该特性。
实际上,我们真正需要的是abort(), timeout可以通过timeout+abort方式来实现,起到真正超时丢弃当前的请求。
而在目前的fetch指导规范中,fetch并不是一个具体实例,而只是一个方法;其返回的promise实例根据Promise指导规范标准是不能abort的,也不能手动改变promise实例的状态,只能由内部来根据请求结果来改变promise的状态。
既然不能手动控制fetch方法执行后返回的promise实例状态,那么是不是可以创建一个可以手动控制状态的新Promise实例呢。所以:
方法一:单纯setTimeout方式
1 | var oldFetchfn = fetch; //拦截原始的fetch方法 |
方法二:利用Promise.race方法
Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变,具体可以参考这里。
1 | var oldFetchfn = fetch; //拦截原始的fetch方法 |
fetch不支持取消请求
我们可以看看AbortController
首先通过AbortController() 构造函数来创建一个controller实例, 然后通过AbortController.signal 属性获取到它的关联对象AbortSignal 的引用.
当 fetch request 初始化后, 将 AbortSignal 作为一个选项传入请求的选项参数中 (如下 {signal}). 这将signal,controller与fetch请求关联起来, 允许我们通过调用AbortController.abort()来取消fetch请求, 正如下第二个事件监听器所示.
1 | var oldFetchfn = fetch; |