朋友:也许面试官是问你什么是fetch呢?我:什么是fetch?
正文
如果不是刷面试题,我真的不知道有fetch这个东西。
FETCH
基本历史
远古时期,XMLHttpRequest对象的出现,JavaScript调用它就可以让浏览器异步地发http请求,然后这项异步技术就被称为Ajax。
之后jQuery封装了它,让异步结果更清晰的表现在一个对象的回调函数属性上,编写方式更简单,但出现了新的问题,回调地狱。
Promise为了解决异步编程的回调地狱问题诞生了。
随后有人把XHR对象用Promise封装了起来,它就是axios库(浏览器端),axios在node.js环境是http模块的封装。
后来又出现了一个可以异步地发http请求的api,就是fetch()。
Fetch它并非是封装xhr对象的库,而是全新的JavaScript的接口。
而且Fetch的api天生就是自带Promise的,现在的Ajax就有了两种方式: XHR对象和Fetch()。
Ajax,Axios,Fetch三者关系
- Ajax 是一种代表异步 JavaScript + XML 的模型(技术合集),所以 Fetch 也是 Ajax 的一个子集
- 在之前,我们常说的 Ajax 默认是指以 XHR 为核心的技术合集,而在有了 Fetch 之后,Ajax 不再单单指 XHR 了,我们将以 XHR 为核心的 Ajax 技术称作传统 Ajax。
- Axios 属于传统 Ajax(XHR)的子集,因为它是基于 XHR 进行的封装。
1 | 现代AJAX包含XHR和Fetch两种类型的常见接口请求方式,AJAX和AXIOS都是基于XHR做的封装。 |
基本使用
fetch()
接受一个 URL 字符串作为参数,默认向该网址发出 GET 请求,返回一个 Promise 对象
基本用法如下:
1 | fetch(url) |
fetch()
接收到的response
是一个 Stream 对象,response.json()
是一个异步操作,取出所有内容,并将其转为 JSON 对象
使用 await 语法改写:
1 | async function getJSON() { |
配置参数
使用示例
fetch()
的第一个参数是 URL,还可以接受第二个参数,作为配置对象,定制发出的 HTTP 请求
fetch(url, options)
- post、put、patch 用法类似
- HTTP 请求的方法、标头、数据体都在
options
这个对象里面设置
下面是一些示例:
(1)POST 请求
1 | const response = await fetch(url, { |
此处的
body
指的是POST 请求的数据体
(2)提交 JSON 数据
1 | const user = { name: 'John', surname: 'Smith' } |
1 Content-Type`的默认值是`'text/plain;charset=UTF-8'
(3)提交表单
1 | const form = document.querySelector('form') |
(5)直接上传二进制数据
fetch()
也可以直接上传二进制数据,将 Blob 或 arrayBuffer 数据放在body
属性里面
1 | let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')) |
完整配置项
1 | const response = fetch(url, { |
由于官网对于部分配置项没有中文翻译,以下列出全部配置项具体值及其说明
cache
:指定如何处理缓存,可能的取值如下:
default
:默认值,先在缓存里面寻找匹配的请求no-store
:直接请求远程服务器,并且不更新缓存reload
:直接请求远程服务器,并且更新缓存no-cache
:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存force-cache
:缓存优先,只有不存在缓存的情况下,才请求远程服务器only-if-cached
:只检查缓存,如果缓存里面不存在,将返回504错误
mode
:指定请求的模式,可能的取值如下:
cors
:默认值,允许跨域请求same-origin
:只允许同源请求no-cors
:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求
credentials
:指定是否发送 Cookie,可能的取值如下:
same-origin
:默认值,同源请求时发送 Cookie,跨域请求时不发送include
:不管同源请求,还是跨域请求,一律发送 Cookieomit
:一律不发送
redirect
:指定 HTTP 跳转的处理方法,可能的取值如下:
follow
:默认值,fetch()
跟随 HTTP 跳转error
:如果发生跳转,fetch()
就报错manual
:fetch()
不跟随 HTTP 跳转,但是response.url
属性会指向新的 URL,response.redirected
属性会变为true
,由开发者自己决定后续如何处理跳转
referrerPolicy
:用于设定Referer
标头的规则,可能的取值如下:
no-referrer-when-downgrade
:默认值,总是发送Referer
标头,除非从 HTTPS 页面请求 HTTP 资源时不发送no-referrer
:不发送Referer
标头origin
:Referer
标头只包含域名,不包含完整的路径origin-when-cross-origin
:同源请求Referer
标头包含完整的路径,跨域请求只包含域名same-origin
:跨域请求不发送Referer
,同源请求发送strict-origin
:Referer
标头只包含域名,HTTPS 页面请求 HTTP 资源时不发送Referer
标头strict-origin-when-cross-origin
:同源请求时Referer
标头包含完整路径,跨域请求时只包含域名,HTTPS 页面请求 HTTP 资源时不发送该标头unsafe-url
:不管什么情况,总是发送Referer
标头
取消Fetch请求
fetch()
请求发送以后,如果中途想要取消,需要使用AbortController
对象,流程如下:
- 创建
AbortController
实例 - 配置对象的
signal
属性指定接收AbortController
实例发送的信号controller.signal
- 使用
controller.abort()
方法发出取消信号 - 发出取消信号后,会触发
abort
事件,这个事件可以监听,也可以通过controller.signal.aborted
属性判断取消信号是否已经发出
1 | let controller = new AbortController() |
Response对象
fetch()
请求成功以后,得到的是一个 Response 对象。它对应服务器的 HTTP 响应
1 | const response = await fetch(url) |
实例属性
Response 实例属性如下表:
属性 | 返回值 | 含义 |
---|---|---|
ok | 布尔值 | 表示请求是否成功,true 对应 HTTP 请求的状态码 200 到 299,false 对应其他的状态码 |
status | 数字 | 表示 HTTP 回应的状态码(如:200,表示成功请求) |
statusText | 字符串 | 表示 HTTP 回应的状态信息(如:请求成功以后,服务器返回”OK”) |
url | 请求的 URL | 如果 URL 存在跳转,该属性返回的是最终 URL |
type | 请求的类型 | basic :普通请求,即同源请求 cors :跨域请求 error :网络错误,主要用于 Service Worker opaque :如果fetch() 请求的type 属性设为no-cors ,就会返回这个值。表示发出的是简单的跨域请求 opaqueredirect :如果fetch() 请求的redirect 属性设为manual ,就会返回这个值 |
redirected | 布尔值 | 表示请求是否发生过跳转 |
body | ReadableStream 对象 | 暴露响应体内容 |
headers | 与响应关联的Headers对象 | 通过访问与响应关联的 Headers 对象,来操作 HTTP 响应头 |
fetch()
发出请求以后,只有网络错误,或者无法连接时,fetch()
才会报错,其他情况都不会报错,而是认为请求成功。这意味着服务器返回的状态码是4xx
或5xx
时,不会报错(Promise 不会变为 rejected
状态)
以下两种方法可以判断是否发生错误:
- 通过
status
属性,得到 HTTP 回应的真实状态码,判断请求是否成功 - 判断
ok
属性是否为true
Response.body
属性是 Response 对象暴露出的底层接口,返回一个 ReadableStream 对象,供用户操作
它可以用来分块读取内容,应用之一就是显示下载的进度
1 | const response = await fetch('flower.jpg') |
response.body.getReader()
方法返回一个遍历器。这个遍历器的read()
方法每次返回一个对象,表示本次读取的内容块,其中:
done
属性是一个布尔值,用来判断有没有读完value
属性是一个 arrayBuffer 数组,表示内容块的内容value.length
属性是当前块的大小
Response 对象还有一个Response.headers
属性,指向一个 Headers 对象,对应 HTTP 回应的所有标头
Headers 对象提供了以下方法,用来操作标头:
Headers.get()
:根据指定的键名,返回键值Headers.has()
: 返回一个布尔值,表示是否包含某个标头Headers.set()
:将指定的键名设置为新的键值,如果该键名不存在则会添加Headers.append()
:添加标头Headers.delete()
:删除标头Headers.keys()
:返回一个遍历器,可以依次遍历所有键名Headers.values()
:返回一个遍历器,可以依次遍历所有键值Headers.entries()
:返回一个遍历器,可以依次遍历所有键值对([key, value]
)Headers.forEach()
:依次遍历标头,每个标头都会执行一次参数函数
方法具体用法请参考官网:developer.mozilla.org/zh-CN/docs/…
实例方法
Response
对象根据服务器返回的不同类型的数据,提供了不同的读取方法
response.text()
:得到文本字符串response.json()
:得到 JSON 对象response.blob()
:得到二进制 Blob 对象
1 | // 读取图片文件flower.jpg,显示在网页上 |
response.formData()
:得到 FormData 表单对象response.arrayBuffer()
:得到二进制 ArrayBuffer 对象
1 | const audioCtx = new window.AudioContext(); |
Stream 对象只能读取一次,这意味着,前五个读取方法,只能使用一个,否则会报错。Response 对象提供了克隆方法
response.clone()
:创建Response
对象的副本,实现多次读取
1 | const response1 = await fetch('flowers.jpg') |
结语
fetch这种方法虽然才了解,但是感觉好像还行,看了很多文章介绍,感觉在文件读取这里似乎有不错的应用。
但是说到底,这终归不是工作中常用的,连面试题问到的似乎都很少,如果不是这里刷到,似乎都没什么人讨论。