Fetch API
回顾一下XMLHttpRequest的问题
- 所有的功能全部集中在一个对象上, 容易书写出混乱而且不容易维护的代码
- 采用传统的事件驱动模式, 无法适配新的 Promise api
Fetch Api的特点
- 并非取代ajax, 而是对ajax传统api的改进
- 精细的功能分割: 头部信息, 请求信息, 响应信息等均分布到不同的对象, 更利于处理各种复杂的ajax场景
- 使用Promise api, 更利于异步代码的书写
- fetch属于web api, 理所当然只能在浏览器端运行
Fetch Api的基本使用
参数
该函数有两个参数:
- 必填: 字符串, 对应的是请求地址
//请求数据的函数如下
const getDefaultData = () => {
//在论坛中我找到了一个请求网易云音乐别人已经写好的接口,可以用来测试
const url = 'https://api.imjad.cn/cloudmusic/?type=song&id=32785674';
fetch(url);
}
// 假设我们的html页面中有一个按钮, 点击这个按钮就会开始请求数据,然后我们现在获取到这个按钮
const btn = document.querySelector('button');
btn.onclick = () => {
getDefaultData();
}
当上面的代码写好, 我们进行点击操作时, 服务器相应的给我们返回了数据
请求配置对象(红色为比较常用的, 绿色为目前不太常用的)
- method: 字符串, 请求方法, 默认值GET
- headers: 对象, 请求头信息
- body: 请求体的内容, 必须匹配请求头中Content-Type
- mode:字符串, 请求模式
- cors: 默认值, 配置为该值, 会在请求头中加入origin和referer
- no-cors: 配置为该值, 将不会再请求头中加入origin和referer, 跨域的时候可能会出现问题
- same-origin: 配置为该值, 则指示请求必须在同一个域中发生, 如果请求其他域, 则会报错
- credentials: 如何携带凭据
- omit: 默认值, 不携带cookie
- same-origin: 请求同源地址时携带cookie
-include: 请求任何地址都要携带cookie
- omit: 默认值, 不携带cookie
- cache: 配置缓存模式
- default: 表示fetch请求之前将检查一下http的缓存
- no-store: 表示fetch请求将完全忽略http缓存的存在, 这意味着请求之前将不再检查http的缓存, 拿到响应以后他也不会再更新http缓存
- no-cache: 如果存在缓存, 那么fetch将发送一个条件查询request和一个正常的request, 拿到响应以后, 他会更新http缓存
- reload: 表示fetch请求之前将忽略http缓存的存在, 但是在请求得到响应以后, 他将主动更新http缓存
- force-cache: 表示fetch请求不顾一切的依赖缓存, 即使缓存过期了, 他依然从缓存中读取, 除非没有任何缓存, 那么他才会发送一个正常的request
- only-if-cached: 表示fetch请求不顾一切的依赖缓存, 即使缓存过期了, 他依然从缓存中读取, 如果没有任何缓存, 那么他将抛出一个错误
//配置对象书写
const getData = function() {
const url = 'xxx/api';
const config = {
method: 'POST', // 写请求方法
headers: {
// 配置请求头
//例如: "Content-Type": 'application/json'
},
body: {
// 配置请求体, 比如POST请求要传递的数据
}
//...其他不常用配置
}
fetch(url, config); // 开始请求数据
}
返回值
fetch函数返回一个Promise对象
- 当收到服务器的返回结果以后, Promise进入resolved状态, 状态数据为Response对象(服务器不一定要200返回, 只要返回了东西都会走resolved状态)
const getDefaultData = async () => {
const url = 'https://api.imjad.cn/cloudmusic/?type=song&id=32785674';
const config = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
body: {
},
// mode: 'cors'
}
// 既然返回的结果是一个Promise, 那么我们就可以用await等待服务器响应的结果
try {
let promise = await fetch(url);
console.log(promise);
}catch(err) {
console.log(err);
}
}
const btn = document.querySelector('button');
//当按钮点击的时候我们开始数据请求
btn.onclick = function () {
getDefaultData();
}
- 当网络发生错误(或者其他导致无法完成交互的错误), Promise进入rejected状态, 状态数据为错误信息
Response对象(服务器响应对象)
- ok: boolean, 当响应消息在200-299之间时为true, 其他为false
- status: number 响应的状态码
- text(): 用于处理文本格式的ajax响应, 他从响应中获取文本流, 将其读完, 然后返回一个被解决为string对象的Promise
const result = await fetch(url, config);
const respText = await result.text();
- blob(): 用于处理二进制的文件格式(比如图片和电子表格)的ajax响应, 他读取文件的原始数据, 一旦读取完整个文件, 就返回一个被解决为blob对象的Promise
const result = await fetch(url, config);
const respBlob = await result.blob();
- json(): 用于处理JSON格式的Ajax的响应, 他讲json数据流转化为一个被解决为JavaScript对象的Promise
const result = await fetch(url, config);
const respJson = await result.json();
- redirect(): 用于重定向到另一个url, 他会创建一个新的Promise, 以解决来自重定向的url响应
响应对象示例
来聊聊Request对象
除了使用基本的fetch方法, 还可以通过创建一个request对象来完成请求, 实际上fetch的内部也会帮你创建一个内部对象
//request对象的创建方式
new Request(url, config)
当我们遇到要复用某些请求的需求时, 就可以用到request对象
function getRespInfo() {
const url = 'xxx/api';
const config = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
}
//...body和其他配置
const request = new Request(url, config);
}
}
function async getData() {
// 每次都走一次getRespInfo这个函数, 保证每次创建的都是一个新的request对象
const result = await fetch(getRespInfo());
const respText = await result.text();
}
注意点: 尽量保证每次请求都是一个新的request对象
再来说说这个Response对象
大多数时候, Response对象都不需要我们自己来构建, 因为服务器会帮我们封装好, 但是在某些特殊时候, 比如后端数据还没写好, 在这种情况下,我们可能最先想到的就是mock数据, 但是我们也可以手动构建一个Response, 用来帮我们构建测试环境
// 我们有一个用来获取省市的函数
async function getProvince() {
// 手动构建的Response对象
const resp = new Response(
`[
{"id": 1, "name": "beijing"},
{"id": 2, "name": "shanghai"}
]`,
ok: true,
status: 200
)
const result = await getJSON(resp);
console.log(result);
}
async function getJSON(resp) {
return await resp.json();
}
但是因为我们大部分时间其实不会这样进行测试, 所以仅了解即可
[扩展] Headers对象
在Request和Response对象内部, 会将传递配置中的headers请求头对象转化为Headers对象
同时Headers对象提供一些方法:
- has(key): 判断请求头中是否存在指定的key
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const resp = new Request(url, config);
console.log(resp.headers.has('Content-Type')); //返回true, 因为我们请求头中配置了
- get(key): 得到请求头中对应的key所对应的值
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json'
}
}
const resp = new Request(url, config);
console.log(resp.headers.get('Content-Type')); //返回'application/json'
- set(key, value): 修改请求头中对应的键值对, 如果是修改未存在的, 则会新建一个键值对
// 其余代码跟上面两个方法一样
resp.headers.set(c, 'helloworld'); //这时候请求头中会多出一个c 并且值为'helloworld'
- append(key, value): 专门用来往请求头中添加键值对
//其余代码跟上面三个方法一样
resp.headers.append(d, 'its tom'); //网请求头中添加一个d, 值为'its tom'
需要注意的是, 如果是对重复的属性用append,并不会覆盖之前的属性而是合并
// 假设headers中有a 值为1,然后我们调用append
resp.headers.append(a, 3); // 这时候headers中a的值为1,2
- keys(): 得到所有的请求头的key所组成的集合(iterator)
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json',
'a': 1
}
}
const resp = new Request(url, config);
console.log(resp.headers.keys());
- values(): 得到所有的请求头key对应的值所组成的集合(iterator)
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json',
'a': 1
}
}
const resp = new Request(url, config);
console.log(resp.headers.values());
- entries(): 得到所有的请求头键值对所组成的集合(iterator)
const url = 'xxx/api';
const config = {
headers: {
'Content-Type': 'application/json',
'a': 1
}
}
const resp = new Request(url, config);
console.log(resp.headers.entries());