目录
3.1 fetch基本请求发送(包括理解、解决fetch涉及的问题)
3.1.1 使用fetch发送get请求(使用try-catch搭配async-await)
3.2.2 x-www-form-urlencoded格式发送
第一章 前言
1.1 注意
该文章用到了小编最近学习技术栈给的一个服务器,是对github获取用户信息的封装,通过请求该服务器获取数据。使用的fetch发送请求,在了解该请求之前一定要对我们常用的promise方法熟练于心,小编也会对给的demo做详细的解释。
1.2 为什么要了解fetch
- 首先先说一下我们现在最常用的axios、jquery封装的请求,它们的"根",或者说本质都是XMLHttpRequest(xhr),相关的ajax请求可以看小编的该文章
- 那么问题来了,如果有一天某公司说,我们的项目不使用xhr系列发送请求(比如,现在大部分还是用的axios请求的),那么我们应该怎么办,我们了解的常用的请求都是axios/jquery,这些是基于xhr的,那么我们应该怎么办,不做了吗?自己写吗?
- 接下来,小编就要为大家介绍一个新的请求方法了,fetch! Fetch API 也是基于 Promise 设计,与xhr是不同类的请求方法;采用了模块化设计,API分散于多个对象中;通过数据流(Stream对象)处理数据,可以分块读取,有利于提高网站性能,对于大文件或者网速慢的场景极为有用
第二章 准备工作
- 开启服务器
- 配置代理跨域
- 理解promise
第三章 fetch相关知识
3.1 fetch基本请求发送(包括理解、解决fetch涉及的问题)
3.1.1 使用fetch发送get请求(使用try-catch搭配async-await)
- 如果fetch() 只接收了一个 url字符串 参数,表示默认向该网址发送 get 请求,会返回一个Promise对象
- 如果需要设置get的参数,直接拼接到 url 地址上即可
- 基本语法:
fetch(url)
.then(...)
.then(...)
.catch(...)
- fetch发送get请求(未优化!!-- 通过小编下面的问题一步一步得到最终优化的代码)
// 1、配置好的接口地址:/api1/search/users2?q=${this.keyWord}
// 2、请求方式:get
// 3、请求参数(可选):连接到路径上,q:keyword
// 发送网络请求---使用fetch发送(未优化)
fetch(`/api1/search/users?q=${this.keyWord}`).then(
// 一定要注意,fetch请求的第一个.then得到的并不是数据,是获取内容
// 我们可以理解成这里能输出内容只能表示与服务器连接成功
// response.json()该参数下的该函数表示是一个异步操作,取出所有的内容,将其转换成JSON对象
response => {
console.log('联系服务器成功了', response)
return response.json()
},
error => {
console.log('联系服务器失败了', error)
}
).then(
// 这里才是通过获取经过res.json() 处理过之后的数据—>正常通过ajax获取到的我们需要的数据
response => { console.log('获取数据成功了', response) },
error => { console.log('获取数据失败了', error) }
)
-- 正常请求成功查看输出:注意我后面用第一个.then与第二个.then区分做描述
第一个.then输出:
第二个.then输出
- 问题一:当第一个.then错误后,还会继续执行第二个.then吗?
答案:会执行,fetch请求我们知道也是使用的promise,promise的.then的链式调用,下图能验证我们的答案是正确的!
- 问题二:为什么第二个.then结果是数据获取成功了,输出undefined,而不是走的error失败呢?
答案:熟悉promise就知道了,首先我们看代码,第一个.then的error里直接就是console.log,没有return值,那么当没有return值时,promise就会给下一个.then返回一个undefined的值,非promise值,状态为fulfilled成功的,下一个.then接收到一个状态为fulfilled的值,肯定就走response那个函数了,输出也是undefined
- 优化一:解决问题上述问题:
在第一个.then错误的代码里面return一个空的promise对象回调--初始化状态的promise实例(promise的解决方法),使得当错误时不会继续执行后面的.then。
fetch(`/api1/search/users2?q=${keyword}`).then(
response => {
console.log('联系服务器成功了')
return response.json()
},
error => {
console.log('炼器服务器失败了', error)
// 返回一个空的promise回调,使得代码不再往后走了
return new Promise(()=>{})
}
).then(
response => {
console.log("获取数据成功", response)
},
error => {
console.log("获取数据失败了", error)
}
)
- 优化二:.then里面一直有 成功、失败,成功、失败的函数,promise有一个更加优化的解决方案.catch捕获错误,只要报错直接就跳到.catch,我们就不需要在每一个.then函数里面都写成功跟失败的回调函数了。
fetch(`/api1/search/users2?q=${keyword}`).then(
response => {
console.log('联系服务器成功了')
return response.json()
}
).then(
response => {
console.log("获取数据成功", response)
}
).catch(
(error) => {console.log("请求出错", error)}
)
- 优化三:(最终结果),请求的时候,针对promise对象,我们为了获取里面的值并不是都用.then获取的,儿时会使用async...await...来处理获取promise里的值,从上面两个.then我们知道应该如何使用了;然后还有注意点,await只能让我们等到成功的结果,那么失败的结果怎么拿到呢,try...catch...解决!代码如下:
// 最终优化
search = async () => { // async...await...搭配使用
try { // try里面实现的是成功的代码逻辑
const response = await fetch(`/api1/search/users2?q=${keyword}`) // 第一个请求获取到了与服务器连接成功的请求结果
const data = await response.json() // 第二个请求拿到的才是服务器返回前端的最终结果值
console.log(data)
} catch (error) { // catch实现的是失败的代码逻辑
console.log(error)
}
}
最终得到结果:
3.1.2 了解Response对象
不要生疏了,这个对象就是我们发送请求时第一个获取到的与服务器连接成功的请求结果,对应服务器的 HTTP 响应。
const response = await fetch(url)
console.log(response)
-
常见属性
属性 | 含义 |
response.ok | 返回一个布尔类型,表示请求是否成功 |
response.status | 返回一个数字,表示HTTP回应的状态码(例如:200,表示已请求成功) |
response.statusText | 返回状态的文本信息(例如:请求成功之后,服务器返回OK) |
response.url | 返回请求的url地址 |
- 常见方法(response .json()最常用)
方法 | 含义 |
response .json() | 得到 JSON 对象 |
response .text() | 得到文本字符串 |
response .blob() | 得到二进制 Blob 对象 |
response .formData() | 得到 FormData 表单对象 |
response .arrayBuffer() | 得到二进制 ArrayBuffer 对象 |
3.1.3 fetch 配置参数
fetch的第一个参数是 url ,此外还可以接收第二个参数,作为配置对象,只配置url默认get请求;可以自定义发出的HTTP请求post、put、patch等,其中:post、put、patch 用法类似,我们以post为例演示
fetch(url,options)
fetch(url,{ // url请求路径
method:'请求方式,比如:post、delete、put', // 请求方法
headers:{
'Content-Type':'数据格式' // 请求携带的数据格式json、x-www-form-urlencoded、formdata……
},
body:'post请求体数据' // 参数
})
3.2 fetch发送post请求
3.2.1 json格式发送
(假设一个表带提交的接口…/add/users2)
// 测试接口(新增操作):
// 接口地址:/api/add/users2
// 请求方式:post
// 请求体参数:
// 1、姓名:name
// 2、年龄:age
// 3、出生:birthday
// post发送:json格式
async addUser() {
let obj = {
name: '❆VE❆',
age: '23',
birthday: '2000-01-01'
}
let response = await fetch('/api/add/users2', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(obj)
})
let data = await response.json()
console.log(data)
}
addUser()
3.2.2 x-www-form-urlencoded格式发送
async function addUser1() {
let response = await fetch(url, {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: 'name=❆VE❆&age=23',
});
let data = await response.json();
console.log(data)
}
addUser1()
3.2.3 formData格式发送
let file = '文件的二进制流'
async function addUser2() {
const formdata = new FormData()
formdata.append('file', file)
let response = await fetch(url, {
method: 'POST',
headers: {
"Content-type": "multipart/form-data",
},
body: formdata
})
let data = await response.json()
console.log(data)
}
addUser2()
3.3 fetch 函数封装
实际开发中,我们不可能对每一个请求都这么写,虽然也能实现,但是毕竟最终造成的代码冗余太多了,对于axios我们会对其进行二次封装,当然了,针对fetch我们是不是也能进行二次封装了呢!如下:
3.3.1 目标效果
不管是什么方式的请求,我们的格式都是如下,区分为两类
// 发送get请求、delete请求
request({
method:'xxx'
url:'xxx',
params:{......}
})
// 发送post请求、put请求、patch请求
request({
method:'xxx'
url:'xxx',
data:{......},
headers: {......}
})
3.1.2 封装函数
async function request(obj) {
// 解构赋值
let { method, url, params, data, headers } = obj
// 判断是否有params参数
// 1、如果有params参数,则把params对象转换成 key=value&key=value的形式,并且拼接到url之后
// 2、如果没有params参数,则不管
if (params) {
// 把对象转换成 key=value&key=value 的方法
// 固定写法:new URLSearchParams(obj).toString()
let str = new URLSearchParams(params).toString()
// console.log(str)
// 拼接到url上
url += '?' + str
}
// 如果没有配置headers,则设置一个最常用的headers
if(headers){
headers = {
'Content-Type': 'application/json'
}
}
// 最终的结果
let response
// 判断是否有data参数,如果有,则需要设置给body,否则不需要设置
console.log(data)
if (data) {
// 如果有data参数,此时直接设置
response = await fetch(url, {
method: method,
headers: headers,
body: JSON.stringify(data)
})
} else {
response = await fetch(url)
}
return response.json()
}
3.1.3 封装发送请求
- get方法发送请求
data = await request({
method: 'get',
url: '/api/search/users2',
params: { // 如果该get请求没有参数则不写,注意自己封装的函数逻辑,参数名别写混了
name: '❆VE❆',
age: '23'
}
})
- post等方法发送请求
data = await request({
method: 'post',
url: '/api/add/user',
data: { // post请求提交参数
name: '❆VE❆',
age: '23'
},
headers: { // 如果请求头需要其他格式,配置即可,不需要封装时则会有默认的配置
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
})