⛽ 概述
XMLHttpRequest
对象的作用是与服务器交互,在不刷新页面的情况下请求特定 URL,获取数据。其实说白了,就是用异步的方式,向服务端发起请求,获取或提交数据,如果非要用通俗又粗俗的话说,它就是用来发ajax
请求的。
🚥 运转流程
首先,先创建一个XMLHttpRequest
实例:
const XHR = new XMLHttpRequest();
然后,调用open()
方法:
const XHR = new XMLHttpRequest();
XHR.open('GET', '/apis');
这行代码会启动一个针对/apis
地址(也可以是绝对路径)的GET
请求,要说明的是:调用open()
方法并不会真正发送请求,而只是启动一个请求以备发送。
要发送真正的请求,必须像下面这样调用send()
方法:
const XHR = new XMLHttpRequest();
XHR.open('GET', '/apis');
XHR.send(null);
这里的send()
方法接收一个参数,既要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null
,因为这个参数对有些浏览器来说是必需的。
因为我们发送的是异步请求,所以要依据readyState
属性的变化,来了解异步的过程。该属性表示请求/响应过程的当前活动阶段,如图所示:
只要readyState
属性的值由一个值变成另一个值,都会触发一次readystatechange
事件。通常,我们只对readyState
值为4的阶段感兴趣,因为这时所有数据都已经就绪。不过,必须在调用open()
之前指定onreadystatechange
事件处理程序才能确保跨浏览器兼容性。
const XHR = new XMLHttpRequest();
XHR.onreadystatechange = () => {
if (XHR.readyState === 4) {
// blah blah blah...
} else { }
}
XHR.open('GET', '/apis');
XHR.send(null);
服务端接收到请求信息,并做出响应(如果通信正常),客户端在收到响应后,响应的数据会自动填充XHR
对象的属性,比如:status
(响应的HTTP状态)、responseText
(作为响应主体被返回的文本)......。
一般来说,可以将HTTP状态码为200作为成功的标志。此外,状态代码为304表示请求的资源并没有被修改,可以直接使用浏览器中缓存的版本,当然,也意味着响应是有效的。
const XHR = new XMLHttpRequest();
XHR.onreadystatechange = () => {
if (XHR.readyState === 4) {
if (XHR.status < 300 || XHR.status === 304) {
// blah blah blah...
} else {
alert(`请求失败了:${XHR.status}`);
}
} else { }
}
XHR.open('GET', '/apis');
XHR.send(null);
此处要说明一下:状态码为304的意思是说浏览器可以用缓存的版本,但是不代表浏览器没有向服务端发起请求。其实,浏览器是真真正正地向服务端发了请求的,只是服务端验证了资源的过期时间后,仅仅是告诉浏览器:“我就不给你数据了,你用你自己那份资源吧,还在保质期呢。”
另外,在接收到响应之前还可以调用abort()
方法,来取消异步请求。在终止请求后,由于内存原因,还应该对XHR
对象进行解引用操作。
上面,是使用XMLHttpRequest
的最简单流程。
下面,再补充一些细节操作:
-
添加/获取头部信息
每个HTTP请求和响应都会带有相应的头部信息(有的有用,有的对开发人员没用),XMLHttpRequest
对象也提供了操作这两种头部(即请求头部和响应头部)信息的方法。
XMLHttpRequest
的setRequestHeader()
方法可以设置自定义的请求头部信息,要成功发送请求头部信息,此方法必须在调用open()
方法之后且调用send()
方法之前调用。
调用XMLHttpRequest
的getResponseHeader()
方法并传入头部字段名称,可以取得相应的响应头部信息。而调用getAllResponseHeaders()
方法则可以取得一个包含所用头部信息的长字符串。这两个方法也要在调用open()
方法之后且调用send()
方法之前调用。
const XHR = new XMLHttpRequest();
XHR.onreadystatechange = () => {
if (XHR.readyState === 4) {
if (XHR.status < 300 || XHR.status === 304) {
// blah blah blah...
} else {
alert(`请求失败了:${XHR.status}`);
}
} else { }
}
XHR.open('GET', '/apis');
XHR.setRequestHeader('请求头的自定义字段名称', '头部字段的值');
XHR.getResponseHeader('响应头的字段名称');
XHR.getAllResponseHeaders();
XHR.send(null);
为什么要添加/获取头信息呢?
此之目的仅仅是为了方便信息的传输。服务器在接收到自定义的头部信息后,可以执行相应的后续操作;同样的,在服务端,也可以利用头部信息向浏览器发送额外的、结构化的数据。而且,不是每次请求都必须做这种操作,要看使用场景,需要用的时候再用。
-
监测进度
XMLHttpRequest
提供了各种在请求被处理期间发生的事件以供监听。这包括定期进度通知、 错误通知,等等。
const XHR = new XMLHttpRequest();
/**
* 监听请求过程中的各种事件
*/
XHR.onprogress = (oEvent) => {
// 服务端到客户端的传输进程(下载)
// 此时readyState=2
if (oEvent.lengthComputable) {
const percentComplete = oEvent.loaded / oEvent.total * 100;
// blah blah blah...
} else {
// 总大小未知时不能计算进程信息
}
};
XHR.onload = (oEvent) => {
// 传输完成,所有数据保存在response中。
// 此时readyState=4
// blah blah blah...
};
XHR.onerror = (oEvent) => {
// 当request遭遇错误时触发。
// blah blah blah...
};
XHR.onabort= (oEvent) => {
// 当request被停止时触发。
// blah blah blah...
};
XHR.onreadystatechange = () => {
if (XHR.readyState === 4) {
if (XHR.status < 300 || XHR.status === 304) {
// blah blah blah...
} else {
alert(`请求失败了:${XHR.status}`);
}
} else { }
}
XHR.open('GET', '/a.txt');
XHR.send(null);
你需要在请求调用
open()
之前添加事件监听。否则progress
事件将不会被触发。
progress
事件同时存在于下载和上传的传输。上面的例子是针对下载的相关事件。上传相关事件在 XMLHttpRequest.upload
对象上被触发,像下面这样:
var oReq = new XMLHttpRequest();
oReq.upload.addEventListener("progress", updateProgress);
oReq.upload.addEventListener("load" , transferComplete);
oReq.upload.addEventListener("error", transferFailed );
oReq.upload.addEventListener("abort", transferCanceled);
oReq.open();
🚗 示例
- 🧪 发送一个
GET
请求
const XHR = new XMLHttpRequest();
XHR.onreadystatechange = () => {
if (XHR.readyState === 4) {
if (XHR.status < 300 || XHR.status === 304) {
console.dir(XHR.response);
} else { }
} else { }
}
XHR.open('GET', '/apis');
XHR.send(null);
结果如图所示:
没有参数的GET请求.png
给请求加点参数:
XHR.open("GET", "/apis?ab=13&page=1");
XHR.setRequestHeader('CC', 989);
XHR.send(null);
结果如图所示:
有参数的GET请求.png
- 🧪 发送一个
POST
请求
const XHR = new XMLHttpRequest();
XHR.onreadystatechange = () => {
if (XHR.readyState === 4) {
if (XHR.status < 300 || XHR.status === 304) {
console.dir(XHR.response);
} else { }
} else { }
}
XHR.open('POST', '/apis');
XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
XHR.send('name=张三丰&职务=掌门');
结果如图所示:
POST请求.png
为什么要添加
Content-Type
请求头信息呢?需要设置
Content-Type
头信息,完全是由于服务端的原因。因为无论是表单、Ajax、jsonp,到了服务端那儿,它只认一种请求,就是form表单请求,所以Ajax发送的POST请求,服务端会认成是form表单的POST请求,而form表单发送POST请求时,如果不设置enctype
属性,就会以默认的application/x-www-form-urlencoded
方式提交表单,因此不加不行啊。如果要上传文件,需要将
Content-Type
头的值设置为multipart/form-data
,总之,跟form表单的提交方式保持一致就对了。
这篇《Express.js 解析 Post 数据类型的正确姿势》介绍了POST请求的四种方式,值得一看👏。
好了,先写到这儿吧。
--(完)--
网友评论