大神请直接划走,如果你对请求模棱两可、或者知道有哪些请求库,但是呢只熟练其中的一两种,并不全会。来,这篇文章就非常适合你。
本文讲解了前端发展史上请求库的一个变迁,从最开始的Ajax
、到后来的jQuery Ajax
、再到fetch
、再到axios
。笔者会详细介绍各个请求库的使用,以及坑点。最后会总结它们各自的优缺点。我相信看完本文,你一定会有所收获。也欢迎各位小伙伴点赞收藏,我相信后面你一定会用得到。
本文所有实例的请求接口在文章末尾,使用 node + express 搭建
在说请求库之前,我们先来回忆一下表单。
大部分情况下,网页中展示的数据或者图片或者多媒体效果都是静态的数据,但是有时用户需要通过网页跟服务器进行动态的数据交互,例如登录、注册等操作,这时需要用到表单。
表单其实是由一些输入框、单选、复选框等元素组成,用户点击提交按钮,浏览器会将这些数据发送给表单设置的 URL ,URL 对应的服务器接收到数据并处理之后,再将数据返回给浏览器。
表单提交方式一般有两种:
第一种是使用type
为submit
的按钮进行提交。
第二种是获取表单,然后手动调用submit()
方法进行提交。
<h3>form1 直接提交h3>
<form action="http://localhost:3000/test" onsubmit="submit1()">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="submit" />
form>
页面渲染如下,点击type="submit"
的提交按钮就可以提交我们的表单。提交后会自动触发我们的onsubmit
属性对应的方法,这里就是会触发submit1
方法。
<h3>form2 手动提交h3>
<form action="http://localhost:3000/test" id="form2" onsubmit="submit1()">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="button" id="submitBtn2" value="提交" onclick="handleSubmit()"/>
form>
在js
里面,我们还可以在按钮的事件里面获取到表单,然后调用表单的submit()
方法进行表单的提交。
handleSubmit = function () {
const form2 = document.getElementById("form2");
form2.submit();
};
页面渲染如下,点击提交按钮,然后手动通过方法submit()
就可以提交我们的表单啦。注意这种方式是不会触发onsubmit
属性对应的方法的。
这里我们是给button
直接绑定的点击事件,当然你也可以通过获取到button
然后给button
绑定事件的方式来进行处理。
// 方式1
const submitBtn2 = document.getElementById("submitBtn2");
submitBtn2.onclick = function () {
const form2 = document.getElementById("form2");
form2.submit();
};
// 方式2
const submitBtn2 = document.getElementById("submitBtn2");
submitBtn2.addEventListener("click", function () {
const form2 = document.getElementById("form2");
form2.submit();
});
因为表单的提交每次都会刷新页面,体验并不是很好,所以后面就出来了Ajax
,Ajax
是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。为了后面能顺利进行Ajax
请求,我们肯定是要把表单提交完后默认进行页面跳转这个行为阻止的。所以我们再来看看阻止表单提交都有哪些方式呢?
阻止表单提交方式一般有三种:
第一种是给onsubmit
绑定的方法返回false
来阻止表单提交。
第二种是给提交按钮返回false
来阻止表单提交。
第三种是阻止提交按钮默认行为来阻止表单提交。
我们先来看看第一种
<form action="http://localhost:3000/test" onsubmit="return submit1()">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="submit" id="submitBtn1" />
form>
在onsubmit
对应的方法里返回false
就可以阻止表单提交了
function submit1() {
return false;
}
<form action="http://localhost:3000/test">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="submit" id="submitBtn2" onclick="return handleSubmit()" />
form>
在提交按钮对应的方法返回false
就可以了。跟上面的有点类似。
function handleSubmit() {
return false;
}
当然你也可以使用绑定事件的方式
<form action="http://localhost:3000/test">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="submit" id="submitBtn2" />
form>
通过给提交按钮绑定事件,然后在事件里面返回false
也能阻止表单提交。注意在addEventListener
绑定事件这种方式下返回false
是无效的。
const submitBtn2 = document.getElementById("submitBtn2");
submitBtn2.onclick = function () {
return false;
};
// 无效 无 效无效 无效
const submitBtn2 = document.getElementById("submitBtn2");
submitBtn2.addEventListener("click", function () {
return false;
});
我们还可以阻止提交按钮的默认事件来阻止表单提交。
<form action="http://localhost:3000/test">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="submit" id="submitBtn2" onclick="handleSubmit()" />
form>
在提交按钮对应的方法里面使用preventDefault
阻止默认事件。
// 这种方式需要window.event来获取事件对象
function handleSubmit(e) {
const event = e || window.event;
event.preventDefault();
}
当然你也可以使用绑定事件的方式
<form action="http://localhost:3000/test">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="submit" id="submitBtn2" />
form>
通过给提交按钮绑定事件,然后在事件里面使用preventDefault
阻止默认事件。
// 方式1
const submitBtn2 = document.getElementById("submitBtn2");
submitBtn2.onclick = function (e) {
e.preventDefault();
};
// 方式2
const submitBtn2 = document.getElementById("submitBtn2");
submitBtn2.addEventListener("click", function (e) {
e.preventDefault();
});
说完表单的提交,我们再来说说表单另外一块非常重要的知识点enctype
enctype
用于定义表单数据提交到服务器的过程中如何对数据进行编码,可选值有:
默认方式是第一种 application/x-www-form-urlencoded
,当使用 form 表单提交数据时,会默认转成key=value&key=value
这种格式的数据。
对于get
请求,会将表单参数生成?key=value&key=value
这样的链接,拼在当前action
链接的后面进行请求。对于post
请求,会将表单参数生成key=value&key=value
这样的链接,放在请求体中进行请求。
并且对于参数中的一些字符(中文、特殊字符等)会进行编码。
为什么要对提交的数据进行编呢?
当使用 form 表单提交数据时,需要对数据进行编码,因为 URL 的数据是通过 ?key=value&key=value&
的方式传输给服务器,这其中有一些作为功能性质的特殊字符例如 & ? =
,如果数据中带有这些字符而且未进行编码的话,可能会导致传输数据发生错误,例如原本是 ?key=value
,如果 value 是 123&456
的话,那么 结果服务器中接收到的 value 值变成123,且多出一个 key=456
,这种编码方式叫做 URL 百分号编码,其标准收录在 RFC3986 中。
当设置成 multipart/form-data
时,浏览器不对字符进行编码,这种编码方式通常适用于上传文件。
对于multipart/form-data
格式的请求数据,会将数据通过特殊标记进行切分,然后放在请求体中进行传递。
使用第三种方式 text/plain
时,浏览器将请求参数放入 body
作为字符串处理,这种方式用于传输原生的 HTTP 报文,不适用任何格式化处理。
接下来我们来看看改变历史的Ajax。因为表单的提交每次都会刷新页面,体验并不是很好,Ajax
是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。所以Ajax
的出现,无疑改变了历史。使用户的体验大大提升。
我们这里说的原生Ajax
主要介绍的是 XmlHttpRequest
,通过 XmlHttpRequest
对象来向服务器发异步请求,从服务器获得数据。
我们先来看看发送一个最简单的Ajax
需要哪些步骤。
首先需要创建请求对象,创建的时候需要考虑浏览器的兼容性。
let xhr;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xhr = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
在发送请求之前,我们最好先对请求进行监听,readystatechange
事件它会在请求的各个阶段被执行。
一般我们只需要关注4这个状态。
// 设置状态监听函数
xhr.onreadystatechange = function () {
// 4成功
if (this.readyState !== 4) return;
// xhr对象,在这里获取响应结果
console.log(this);
};
当一个 XMLHttpRequest
请求被 abort()
方法取消时,其对应的 readystatechange
事件不会被触发。
XMLHttpRequest.open(method,url,async) 方法初始化一个新创建的请求,或重新初始化一个请求。
xhr.open(
"get",
`http://localhost:3000/testGet?name=randy&age=27` // 接口在文章末尾,使用node + express搭建
);
创建完请求后,我们还需要设置响应数据的类型,如果不设置将会使用默认值text
。
常用返回值格式如下
这里我们响应是json
,所以我们需要设置为josn
。
xhr.responseType = "json";
XMLHttpRequest.send()
方法用于发送 HTTP 请求。如果是异步请求(默认为异步请求),则此方法会在请求发送后立即返回;如果是同步请求,则此方法直到响应到达后才会返回(也就是会阻止后续代码执行)。
XMLHttpRequest.send()
方法接受一个可选的参数,其作为请求主体;如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null。
这里因为我们是get
请求,所以我们发送一个null就可以了。
xhr.send(null)
send()
方法支持的数据格式如下,在post
请求时会用到。
XMLHttpRequest.send();
XMLHttpRequest.send(String); // 传递js对象需要转成json字符串
XMLHttpRequest.send(URLSearchParams data);
XMLHttpRequest.send(FormData data);
XMLHttpRequest.send(ArrayBuffer data);
XMLHttpRequest.send(ArrayBufferView data);
XMLHttpRequest.send(Blob data);
XMLHttpRequest.send(Document data);
XMLHttpRequest.send(DOMString? data);
这些请求数据格式需要熟知,不然传递不支持的数据格式会导致请求出错!
响应其实是在我们的onreadystatechange
监听事件里面,我们来看看成功的xhr
对象是怎么样的。
这里我们重点关注response、status、statusText、readyState
这几个值即可。
response
属性返回响应的正文。返回的类型为 ArrayBuffer
、Blob
、Document
、JavaScript Object
或 字符串中的一个。这取决于请求的 responseType
属性。如果没有指定,结果默认是text
类型。
status
是http状态码。
statusText
是http状态信息。
readyState
是当前请求对象的状态。
前面我们举了一个最简单的get
请求例子,下面我们再深入了解下。比如post请求、异步、取消请求、错误处理等。
对于post
请求的参数是需要放在send()
方法里面,并且支持多种数据格式。下面笔者使用频率最高的三种数据格式来进行讲解。
我们先来创建一个表单,这里我们不再使用表单来提交数据,而是使用Ajax
。所以这个表单只是方便我们获取用户输入的数据。(当然你也可以不使用表单)。
<h3>form1h3>
<form id="form1">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="button" id="submitBtn1" onclick="request1()" value="提交" />
form>
<h3>结果h3>
<div id="result">div>
我们再在请求方法request1
中创建一个xhr
对象,并初始化请求和监听请求。
function request1() {
// 创建xhr
let xhr;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xhr = new XMLHttpRequest();
} else {
// IE6, IE5 浏览器执行代码
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
// 设置状态监听函数
xhr.onreadystatechange = function () {
// 4成功
if (this.readyState !== 4) return;
// xhr对象
console.log(this);
// 将响应结果放到div显示
const resultDiv = document.getElementById("result");
resultDiv.innerText = this.response;
};
// 初始化请求
xhr.open("post", `http://localhost:3000/testPost`);
}
请求体默认的数据格式是text/plain
,对于这种数据我们开发中并不常用,一般会使用下面这三种数据格式,那怎么修改我们请求的数据格式呢?那就得使用setRequestHeader
方法了,也就是设置我们的请求头。
// 设置我们请求数据的格式
// 对于 发送数据方式2,这个设置请求数据可以免去,因为它会自动设置
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// 获取表单数据
const formData = new FormData(document.getElementById("form1"));
const name = formData.get("name");
const age = formData.get("age");
// 发送数据1
xhr.send(`name=${name}&age=${age}`);
// 发送数据方式2 直接借助 URLSearchParams 免去手动拼接参数
// const urlSearchParams = new URLSearchParams(formData);
// xhr.send(urlSearchParams);
我们来看看post
请求效果,结果成功返回。
// 设置请求数据类型
xhr.setRequestHeader("Content-type","application/json");
// 获取表单数据
const formData = new FormData(document.getElementById("form1"));
const name = formData.get("name");
const age = formData.get("age");
// 发送json数据 需要转成字符串
xhr.send(JSON.stringify({ name, age }));
我们来看看post
请求效果,结果成功返回。
我们一般使用这种数据格式来上传文件,并不会直接用来提交简单表单数据。
// 可以不设置 因为new FormData会自动将请求头设置为这种格式
xhr.setRequestHeader("Content-type","multipart/form-data");
// 获取表单数据
const formData = new FormData(document.getElementById("form1"));
// 初始化请求 使用第二个接口 使用multer特殊处理
xhr.open("post", `http://localhost:3000/testPost2`);
// 发送json数据 需要转成字符串
xhr.send(formData);
我们来看看post
请求效果,结果也成功返回。
Ajax
默认是异步的,当我们open
方法的第三个参数设置为false
的时候,会将我们的请求设置为同步。很多小伙伴以为同步就是响应结果是send
方法的返回值,其实并不是的。这里的同步和异步只是代表阻塞不阻塞后续代码的运行。响应结果都是需要在readystatechange
监听事件中获取的。
我们来看两个例子
先来看看异步
xhr.open(
"get",
`http://localhost:3000/testGet?name=${name}&age=${age}`,
true
);
const result = xhr.send(null);
console.log(result);
console.log("后续代码");
来看看输出,异步请求是请求后不会阻塞后续代码运行,并且send
方法是不会返回结果的。
再来看看同步
xhr.open(
"get",
`http://localhost:3000/testGet?name=${name}&age=${age}`,
false
);
// 阻塞后续代码运行
const result = xhr.send(null);
console.log(result);
console.log("后续代码");
来看看输出,同步请求是会阻塞后续代码的运行的,直到请求有了结果后续代码才会运行,并且send
方法是不会返回结果的。
如果该请求已被发出,XMLHttpRequest.abort() 方法将终止该请求。当一个请求被终止,它的 readyState
将被置为 0,并且请求的 status
置为 0。
xhr.abort();
当请求遇到错误时,将触发error
事件。不过需要注意的是后端返回的错误并不会触发onerror
事件,比如后端返回的500 400
等。只有在网络中断或者跨域时才会触发onerror
事件。
// 设置请求失败时的监听函数
// 当网络中断或者跨域时会触发onerror事件
xhr.onerror = function () {
console.error(this);
};
jQuery Ajax
并不是一个新东西,而是对原生Ajax
的一个封装,使其使用起来更方便、开发起来更快速。
$.ajax()
之前使用ajax
发送一个请求,需要经过创建、监听、初始化请求、发送请求等一系列步骤,非常麻烦。在jQuery
中,一个方法就能搞定。
我们来看个例子。
首先在页面引入jQuery
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">script>
然后创建一个表单。
<h3>form1h3>
<form id="form1">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="button" id="submitBtn1" onclick="request2()" value="提交" />
form>
<h3>结果h3>
<div id="result">div>
当点击提交按钮的时候,我们发送一个get
请求到后端服务器。
function request2() {
$.ajax({
url: "http://localhost:3000/testGet", // 规定发送请求的 URL。默认是当前页面。
data: $("#form1").serialize(), // 规定要发送到服务器的数据。
success(result) {
console.log(result);
$("#result").text(JSON.stringify(result));
},
});
}
我们来看效果。数据正常获取并返回
下面我们以post
请求为例,来详细讲解下$.ajax()
方法。
function request2() {
const formData = new FormData(document.getElementById("form1"));
const name = formData.get("name");
const age = formData.get("age");
$.ajax({
type: "POST", // 规定请求的类型(GET 或 POST) 默认GET
url: "http://localhost:3000/testPost2", // 规定发送请求的 URL。默认是当前页面。
// data: formData, // 上传文件需要用到,传送formData会自动设置contentType: "multipart/form-data",我们需要设置contentType: false、processData: false
// data: urlSearchParams, // 直接传递 URLSearchParams 对象 我们需要设置processData: false
data: `name=${name}&age=${age}`, // 规定要发送到服务器的数据。支持这种search字符串类型。可以使用$("#form1").serialize()直接序列化出来
// data: $("#form1").serialize(), // 把表单序列化成search字符串
// data: $.param({ name, age }), // 把对象序列化成search字符串
// data: { name, age }, // 支持对象类型
// data: JSON.stringify({ name, age }), // 支持对象字符串类型。使用这种方式需要设置 contentType: "application/json"
// contentType: "application/x-www-form-urlencoded", // 发送数据到服务器时所使用的内容类型。默认是:"application/x-www-form-urlencoded"。
// contentType: "application/json", // 发送数据到服务器时所使用的内容类型。默认是:"application/x-www-form-urlencoded"。
// contentType: false, // 不设置,当data是FormData是需要设置。
dataType: "", // 预期的服务器响应的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断。可以设置json、text、xml、html等
// processData: false, // 布尔值,规定通过请求发送的数据是否转换为查询字符串。默认是 true。
beforeSend(xhr) {
// 发送请求前运行的函数
console.log(xhr);
},
success(result) {
// 请求成功运行的函数
console.log(result);
$("#result").text(JSON.stringify(result));
},
error(xhr, status, error) {
// 如果请求失败要运行的函数
// 后端返回错误码也会进入该方法 比如 400/401/500
console.log(error);
},
complete(xhr, status) {
// 请求完成时运行的函数,不管成功或者失败
console.log(status);
},
});
}
这里我们重点关注data
、contentType
、processData
。
data
只支持URLSearchParams
、 FormData
、name=${name}&age=${age}
这种search字符串、对象、对象字符串五种形式。并且需要和contentType
相对应。
当contentType: "application/json"
时传递的data
只能是对象字符串形式。
当contentType: "application/x-www-form-urlencoded"
时传递的data
可以是search字符串还可以是对象,因为对象会被自动转化成search字符。
search字符串我们可以手动拼出来,当然还有更便捷的方法,那就是使用$.serialize()和$.param()
方法。
data: $("#form1").serialize() // 表单序列化
data: $.param({ name, age }) // 对象序列化
当我们传递的data
参数是URLSearchParams
格式时,需要将processData
设置为false
,关闭自动转换。否则会报错。
当我们传递的data
参数是FormData
格式时(一般是在上传文件的时候),需要将processData
设置为false
,关闭自动转换。并且还需要将contentType
设置为false
。否则会报错。
当然如果你觉得上面的$.ajax()
方法太麻烦,我们还可以使用对应请求的便捷方法$.get()
、$.post()
、$.getJSON()
。
$.get()
$.get(URL,data,function(data,status,xhr),dataType)
方法使用 HTTP GET 请求从服务器加载数据。它有四个参数。
URL 必需。规定您需要请求的 URL。
data 可选。规定连同请求发送到服务器的数据。只支持查询字符串和对象两种类型。
function 可选。规定当请求成功时运行的函数。
dataType 可选。规定预期的服务器响应的数据类型。默认地,jQuery 会智能判断
这里我们简单试用下
$.get(
"http://localhost:3000/testGet",
$("#form1").serialize(),
function success(data, status, xhr) {
console.log(data);
$("#result").text(JSON.stringify(data));
},
"json"
);
能正确获取到结果
$.post()
$.post(URL,data,function(data,status,xhr),dataType)
方法使用 HTTP POST 请求从服务器加载数据。它有四个参数。
URL 必需。规定您需要请求的 URL。
data 可选。规定连同请求发送到服务器的数据。只支持查询字符串和对象两种类型。
function 可选。规定当请求成功时运行的函数。
dataType 可选。规定预期的服务器响应的数据类型。默认地,jQuery 会智能判断
这里我们简单试用下
$.post(
"http://localhost:3000/testPost",
$("#form1").serialize(),
function success(data, status, xhr) {
console.log(data);
$("#result").text(JSON.stringify(data));
},
"json"
);
能正确获取到结果
$.getJSON()
$.getJSON(url,data,function(data,status,xhr))
方法使用 AJAX 的 HTTP GET 请求获取 JSON 数据。它有三个参数。
URL 必需。规定您需要请求的 URL。
data 可选。规定连同请求发送到服务器的数据。只支持查询字符串和对象两种类型。
function 可选。规定当请求成功时运行的函数。
这里我们简单试用下
$.getJSON(
"http://localhost:3000/testGet",
$("#form1").serialize(),
function success(data, status, xhr) {
console.log(data);
$("#result").text(JSON.stringify(data));
}
);
能正确获取到结果
可以发现,虽然便捷方法使用起来更方便,但是限制还是比较多,特别是数据传递格式那块,只支持查询字符串和对象两种类型。并且对于请求错误也没有回调函数,只有成功的回调函数。
jQuery Ajax
会自动根据返回结果进行设置。fetch(url, config)
方法用于发起获取资源的请求。它返回一个 promise
,这个 promise
会在请求响应后被 resolve
,并传回 Response
对象。
fetch()
并不是对原生Ajax
的封装,而是浏览器原生支持的一个请求库。
我们先来看一个简单的例子
fetch(`http://localhost:3000/testGet?name=randy&age=27`, {})
.then((response) => {
console.log(response);
return response.json();
})
.then((res) => {
console.log(res);
});
输出response
和res
可以发现,fetch()
请求完后并不是直接返回结果,而是一个Response 对象。它对应服务器的 HTTP 回应。这个对象我们后面会重点介绍。response
是一个 Stream
对象,我们需要调用它的一些方法才能得到我们想要的数据。比如上面我们调用了response.json()
,这个方法的功能就是将数据转换成json
格式的数据。
Response.ok
属性返回一个布尔值,表示请求是否成功,true
对应 HTTP 请求的状态码 200 到 299,false
对应其他的状态码。
Response.status
属性返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)。
Response.statusText
属性返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回"OK")。
Response.url
属性返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。
Response.type
属性返回请求的类型。可能的值如下:
basic
:普通请求,即同源请求。cors
:跨域请求。error
:网络错误,主要用于 Service Worker。opaque
:如果fetch()
请求的type
属性设为no-cors
,就会返回这个值。opaqueredirect
:如果fetch()
请求的redirect
属性设为manual
,就会返回这个值。Response.redirected
属性返回一个布尔值,表示请求是否发生过跳转。
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()
:依次遍历标头,每个标头都会执行一次参数函数。比如获取响应Content-Type
response.headers.get('Content-Type'); // application/json; charset=utf-8
前面我们说了,响应内容是在response.body
上面,但是body
是一个ReadableStream
对象,我们需要进一步转换才能得到我们想要的值。
那转换都有哪些方法呢?我们可以根据服务器返回的不同类型的数据,调用不同的读取方法。
response.text()
:得到文本字符串。response.json()
:得到 JSON 对象。response.blob()
:得到二进制 Blob 对象。response.formData()
:得到 FormData 表单对象。response.arrayBuffer()
:得到二进制 ArrayBuffer 对象。fetch(url, config)
有两个参数,url
是请求地址,这个就不再多说了,我们重点来看看config
。
这里笔者将介绍几个比较重要的config
参数。
method:HTTP 请求的方法,GET
、POST
、DELETE
、PUT
都在这个属性设置。
headers:一个对象,用来定制 HTTP 的标头。比如常用的content-type
。
body:POST 请求的数据体。Get和Head请求不能设置该参数。
我们来看看fetch支持哪些请求数据类型呢?我们来看笔者测试的例子。
fetch(`http://localhost:3000/testPost`, {
method: "POST",
// body: "纯文本",
// body: formData, // 会自动设置 content-type 为 multipart/form-data
// body: urlSearchParams, // 会自动设置 application/x-www-form-urlencoded
// body: `name=${name}&age=${age}`, // 需要手动设置 "content-type": "application/x-www-form-urlencoded"
body: JSON.stringify({ name, age }), // 需要手动设置 "content-type": "application/json"
// 还支持 Blob 或 arrayBuffer,使用的比较少 这个笔者就不举例了
headers: {
// "content-type": "application/x-www-form-urlencoded",
"content-type": "application/json",
},
})
.then((response) => {
console.log(response);
// 转成字符串文本
return response.text();
})
.then((res) => {
const resultDiv = document.getElementById("result");
resultDiv.innerText = res;
});
fetch
支持 text、FormData、URLSearchParams、search字符串、json字符串、Blob、arrayBuffer
格式的请求数据类型,并且body
默认情况下会以'text/plain;charset=UTF-8
格式发送请求数据。
当数据格式为FormData
时会自动设置 content-type
为 multipart/form-data
的请求头。
当数据格式为URLSearchParams
时会自动设置 content-type
为 multipart/x-www-form-urlencoded
的请求头。
传递json
格式数据需要转成json
字符串,并且还需要设置 "content-type": "application/json"
的请求头。
mode:指定请求的模式。可取值有三个:cors、same-origin、no-cors
cors
:默认值,允许跨域请求。same-origin
:只允许同源请求。no-cors
:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的简单请求。cache
属性指定如何处理缓存。可取值如下:
default
:默认值,先在缓存里面寻找匹配的请求。no-store
:直接请求远程服务器,并且不更新缓存。reload
:直接请求远程服务器,并且更新缓存。no-cache
:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存。force-cache
:缓存优先,只有不存在缓存的情况下,才请求远程服务器。only-if-cached
:只检查缓存,如果缓存里面不存在,将返回504错误。credentials
属性指定是否发送 Cookie。可能的取值如下:
same-origin
:默认值,同源请求时发送 Cookie,跨域请求时不发送。include
:不管同源请求,还是跨域请求,一律发送 Cookie。omit
:一律不发送。fetch()
发出请求以后,有一个很重要的注意点:只有网络错误,或者无法连接时,fetch()
才会报错,其他情况都不会报错,而是认为请求成功。这就是说,即使服务器返回的状态码是 4xx 或 5xx,fetch()
也不会报错(即 Promise 不会变为 rejected
状态)。
那怎么判断请求是否成功
第一种方法是通过Response.status
属性,得到 HTTP 回应的真实状态码,判断请求是否成功。也就是判断status是否在200-300之间。
async function fetchTest() {
let response = await fetch(`http://localhost:3000/testGet?name=randy&age=27`);
if (response.status >= 200 && response.status < 300) {
// 请求成功
return await response.json();
} else {
// 请求失败
throw new Error(response.statusText);
}
}
另外一种方法就是判断response.ok
是否为true
。因为status
在200-300
之间的时候ok
的值是true
。
if (response.ok) {
// 请求成功
} else {
// 请求失败
}
fetch()
请求发送以后,如果中途想要取消,也是支持的,需要使用AbortController
对象。
下面我们来演示一下
let controller = new AbortController();
let signal = controller.signal;
fetch(`http://localhost:3000/testGet?name=${name}`, {
signal: controller.signal, // 需要传递signal参数
});
// 监听取消
// 也可以通过`controller.signal.aborted`属性判断取消信号是否已经发出。
signal.addEventListener("abort", () => console.log("signal abort!"));
// 取消
controller.abort();
这里需要注意的是controller、signal
和fetch
请求参数里面的signal
是一一对应的。你调用哪个controller.abort()
就会取消对应的请求。当然你也可以使用同一个controller
对应多个请求,这样你就可以一次取消多个请求。
promise
,使用更方便、快捷、高效。我们再来看看目前使用最多的axios
axios
并不是新东西,而是一个基于 promise 对原生 Ajax 封装的 HTTP 库,可以用在浏览器和 node.js 中。
所以在使用之前我们需要先进行引入
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
我们下来看一个简单的例子
<h3>axios formh3>
<form id="form1">
<input type="text" name="name" placeholder="请输入名字" />
<input type="number" name="age" placeholder="请输入年龄" />
<input type="button" id="submitBtn1" onclick="request4()" value="提交" />
form>
<h3>结果h3>
<div id="result">div>
我们使用axios(config)
发送一个请求。
function request4() {
const formData = new FormData(document.getElementById("form1"));
const name = formData.get("name");
const age = formData.get("age");
const urlSearchParams = new URLSearchParams(formData);
axios({
url: "http://localhost:3000/testGet",
params: urlSearchParams,
}).then((res) => {
// 响应数据在data中
console.log(res.data);
const resultDiv = document.getElementById("result");
resultDiv.innerText = JSON.stringify(res.data);
});
}
我们来看看效果
对于axios
,重点在config
和响应结果response
,下面我们来逐一分析。
这里笔者介绍几个比较重要的config
参数。
url
是用于请求的服务器 URL
HTTP 请求的方法,GET
、POST
、DELETE
、PUT
都在这个属性设置。
baseURL
将自动加在 url
前面,除非 url
是一个绝对 URL。通过设置baseURL
可以简化我们url
的配置。
设置请求头,比如常用的content-type
。这个我们就不多说了。
params
是即将与请求一起发送的 URL 参数。必须是一个无格式对象(plain object)或 URLSearchParams 对象。
params
里面设置的参数axios会自动转成search字符串,拼接到请求地址后面。
data
是作为请求主体被发送的数据。只适用于这些请求方法 ‘PUT’, ‘POST’, 和 ‘PATCH’。
在没有设置 transformRequest
时,必须是以下类型之一:
当数据格式为FormData
时 axios 会自动设置 content-type
为 multipart/form-data
的请求头。
当数据格式为URLSearchParams
时 axios 会自动设置 content-type
为 application/x-www-form-urlencoded
的请求头。
当数据格式为plain object
时 axios 会自动设置 content-type
为 application/json
的请求头。
当你想以application/x-www-form-urlencoded
格式传送数据的时候,通常有三种方法。
URLSearchParams
参数。axios 会自动设置 content-type
为 application/x-www-form-urlencoded
的请求头。plain object
,但是需要你手动设置 content-type
为 application/x-www-form-urlencoded
的请求头。qs
库生成search字符串进行传递。因为当数据格式为string
时 axios 会自动设置 content-type
为 application/x-www-form-urlencoded
的请求头。当你想发送纯字符串,需要手动设置请求头content-type
为 text/plain
。
responseType
表示服务器响应的数据类型,可以是 ‘arraybuffer’, ‘blob’, ‘document’, ‘json’, ‘text’, ‘stream’。设置完后,axios
会自动对我们的响应结果进行转换。
timeout
指定请求超时的毫秒数(0 表示无超时时间)。如果请求花费了超过 timeout
的时间,请求将被中断
withCredentials
表示跨域请求时是否需要携带cookie
。默认是false
。
maxContentLength
定义允许的响应内容的最大尺寸
配置弄懂之后,我们再来看看响应response
data
由服务器提供的响应。我们的响应数据就是在这个属性里面获取。
status
来自服务器响应的 HTTP 状态。比如成功会返回200。
statusText
来自服务器响应的 HTTP 状态信息。比如成功会返回"OK"
。
headers
服务器响应的头
请求的配置信息。
请求对象,因为axios
是对XMLHttpRequest
的包装,所以是一个XMLHttpRequest
对象。
我们一般不会直接使用axios
,而是创建axios
实例。axios
支持使用自定义配置新建一个 axios
实例。
const instance = axios.create({
baseURL: '/api',
timeout: 1000,
headers: {'content-type': 'application/json'}
});
// ...
创建实例有两个好处
第一,相同配置我们可以在创建实例的时候配置好,这样后面就不需要重复在每个请求里面配置了。
第二,我们一个系统可以有多个请求实例,每个实例可以有自己独特的配置。
每个axios
实例都能有自己的request
请求方法,并且还有便捷方法。
这里的config
会和上面创建实例使用的config
合并,相同配置会覆盖。
这个方法类似axios(config)
。
直接发送一个get请求
直接发送一个delete请求
直接发送一个head请求
直接发送一个options请求
直接发送一个post请求,第二个参数是请求数据。
直接发送一个put请求,第二个参数是请求数据。
直接发送一个patch请求,第二个参数是请求数据。
axios
还支持并发请求。
比如一次性发送两个请求。
axios
.all([
axios.get("http://localhost:3000/testGet", {
params: { name: "jack" },
}),
axios.get("http://localhost:3000/testGet", {
params: { name: "tom" },
}),
])
.then(
axios.spread((res1, res2) => {
console.log(res1);
console.log(res2);
})
)
.catch((err) => {
console.log(err);
});
通过axios.all([请求1, 请求2...])
批量发送请求,通过axios.spread((请求1结果,请求2结果...) => {})
获取请求结果。注意当请求中有一个失败整个请求都失败,在catch
里面会返回错误的那个promise
。
axios
会自动分析响应的 http status
码,当status
在200到300之间话,会reslove
,也就是成功,其它会被reject
。
当然如果你想自定义成功status
范围的话,可以通过配置请求参数validateStatus
方法
validateStatus: function (status) {
return status >= 200 && status < 300; // 默认是200-300
},
对于错误的处理,一般有两种方式
// 通过 promise 的 catch
axios("http://localhost:3000/testGet", {
params: urlSearchParams,
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
// 通过 try catch
try {
const res = await axios("http://localhost:3000/testGet", {
params: urlSearchParams,
});
console.log(res);
} catch (err) {
console.log(err);
}
axios()
请求发送以后,如果中途想要取消,也是支持的,需要使用CancelToken
对象。总体来说和fetch
的配置很类似。
下面我们来演示一下
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios("http://localhost:3000/testGet", {
cancelToken: source.token, // 需要传递cancelToken
});
// 取消请求(message 参数是可选的)
source.cancel("Operation canceled by the user.");
这里需要注意的是CancelToken
和axios
请求参数里面的cancelToken
是一一对应的。你调用哪个source.cancel()
就会取消对应的请求。当然你也可以使用同一个cancelToken
对应多个请求,这样你就可以一次取消多个请求。
axios
还支持第二种取消请求的方式
const CancelToken = axios.CancelToken;
let cancel;
axios("http://localhost:3000/testGet", {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
}),
});
// 取消请求(message 参数是可选的)
cancel("Operation canceled by the user.");
axios
相较fetch
好像没有取消的监听函数。
promise
,使用更方便、快捷、高效。responseType
,自动将我们的响应转换成相应的格式,基本不需要手动处理。content-type
的值,非常智能。如果对axios
在TypeScrit
中的应用感兴趣的话可以看看笔者之前写的TypeScript实战之用TS封装Axios
接口很简单,使用 node + express
搭建
const express = require("express");
const app = express();
var cors = require("cors");
// 允许跨域
app.use(cors());
// 解析表单中 application/x-www-form-urlencoded 格式的数据
app.use(express.urlencoded({ extended: false }));
// 解析表单中application/json格式的数据
app.use(express.json());
// 解析表单中multipart/form-data格式的数据
const multer = require("multer");
const upload = multer();
app.get("/testGet", function (req, res, next) {
res.json(req.query);
});
app.post("/testPost", function (req, res, next) {
// 将请求体数据直接返回
res.send(req.body);
});
// 特殊处理multipart/form-data格式的数据
app.post("/testPost2", upload.none(), function (req, res, next) {
// 将请求体数据直接返回
res.send(req.body);
});
app.listen(3000, () => {
console.log("serve running on 3000");
});
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!