如今我们前端参与的项目大部分是前后端分离的项目,后端提供接口,前端调用接口,前端要完成客户端与服务器的数据交互(即:前后端的数据交互),如此JavaScript异步通信就非常重要,前端实现异步通信可以用AJAX代称。
AJAX不是一种编程语言,它是一种技术或者说一种方法,翻译过来是异步的 JavaScript 和 XML,AJAX是一种异步的客户端和服务端进行交换数据的方法,是在不刷新页面的情况下请求特定 URL,获取数据。
传统Ajax 指的是 XMLHttpRequest(XHR)目前常用的异步通信技术如JQuery的ajax,axios都是对XHR的二次封装,浏览器提供了一个构造函数XMLHttpRequest。
用XMLHttpRequest构造函数实例化一个XMLHttpRequest 对象,通过XMLHttpRequest 对象与服务器进行HTTP通信,同步或异步地返回 Web 服务器的响应,并以文本的形式返回内容。
PS:本文中说的XMLHttpRequest,指的是XMLHttpRequest Level 2,XMLHttpRequest Level 2 使用指南
下面我用fastmock模拟一个接口,演示一次异步HTTP请求
var xhr=new XMLHttpRequest();
xhr.open("GET","https://www.fastmock.site/mock/d6b39fde63cbe98a4f2fb92ff5b25a6d/api/getOne?id=1",true);
xhr.send();
xhr.onload =function(){
console.log(xhr);
}
PS:
1、进行HTTP通信时,遵从HTTP协议,HTTP协议是超文本传输协议(HTTP,HyperText Transfer Protocol)是浏览器与Web服务器之间的应用层通信协议,HTTP协议交互的信息被称为HTTP报文。
2、由于同源策略(基于浏览器的安全的一种约定),默认情况下浏览器不容许XMLHttpRequest进行跨域(协议、域名、端口只要有一个不同即是跨域)请求。
浏览器同源策略及跨域的解决方法
XMLHttpRequest对象有个属性readyState在初次创建时为0,之后这个值随着交互的阶段不同会随时增加,readyState值的变化会触发onreadystatechange事件,当readyState值变为4时触发onload事件。
首先要用XMLHttpRequest对象的open()方法初始化 HTTP 请求参数,例如设置 URL 和 HTTP 方法,但并不发送请求。
xhr.Open(method,url,asynch,username,password)
参数 | 说明 |
---|---|
method | http请求的方法,最常用的是GET、POST、PUT、DELETE等,默认为GET |
url | 请求的服务器的地址,必选项 |
asynch | 是否采用异步方法,布尔型,true为异步,false为同步,同步请求会阻塞js主线程,默认为false。 |
username | 提供http认证机制需要的用户名 |
password | 提供http认证机制需要的密码 |
常用的HTTP请求方法
可以使用SetRequestHeader(header,Value)方法,为HTTP请求设置请求头,请求头包含了对客户端的环境描述、客户端请求的主机地址等信息。
设置HTTP请求中的指定头部header的值为value.
此方法需在open方法以后调用,一般在post方式中使用。
xhr.setRequestHeader("Cache-Control","no-cache"); //设置禁止缓存请求结果
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); // 设置客户端提交给服务器文本内容的编码方式
常用的HTTP请求头
使用send(content)方法发送,GET请求没有主体,应传递null或省略参数
var xhr = new XMLHttpRequest()
xhr.open('GET', 'https://www.fastmock.site/mock/d6b39fde63cbe98a4f2fb92ff5b25a6d/api/getOne?id=1')
xhr.send()
xhr.onload = function () {
console.log(xhr)
}
POST请求通常拥有主体,并必须使用setRequestHeader()指定“Content-Type”头
var xhr=new XMLHttpRequest();
xhr.open("POST","https://www.fastmock.site/mock/d6b39fde63cbe98a4f2fb92ff5b25a6d/api/add",true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("id=1&title=标题");
xhr.onload =function(){
console.log(xhr);
}
XHR发送请求后,经过服务器的处理并返回数据成功的话,XMLHttpRequest对象status属性值一般是200,,readyState属性为4,并触发onload事件,这时候可以在XHR对象中取得服务器的响应数据。
var xhr=new XMLHttpRequest();
xhr.open("GET","https://www.fastmock.site/mock/d6b39fde63cbe98a4f2fb92ff5b25a6d/api/getOne?id=1",true);
xhr.send();
xhr.onload =function(){
console.log(xhr)
//http状态码为200,表示请求成功
if(xhr.status == 200){
console.log(JSON.parse(xhr.response)); //将json字符串解析成json对象
}
}
只读,status返回HTTP的响应状态码;statusText属性返回Http状态码的文本信息。
HTTP状态码详解
只读,这三个属性返回的是服务器的响应数据。
返回响应类型。允许我们手动的设置返回数据的类型。将它设置为一个空字符串,它将使用默认的"text"类型。
只读,返回经过序列化的响应 URL,如果该 URL 为空,则返回空字符串。
PS:虽然AJAX被称作异步的 JavaScript 和 XML,但是JSON字符串是数据交互时最常用的数据格式,不过,具体传递什么格式的数据要看前后端的约定。
JSON.parse()将JSON字符串转化为 js 对象
现实中,不可能一直都是完美的http请求和响应,总会遇到各种各样的问题,比如服务器挂掉了,资源地址变化了,数据库出问题了,网络断了等等,这时候往往需要给用户一个反馈告知。
这里的异常指的是HTTP连接异常,而不是应用的数据异常,也就是与HTTP状态码有关的异常。
在下面的test.php中,我设置2秒后返回结果,js代码设置xhr.timeout = 1000,这样请求就会超时,进而触发ontimeout事件,超时时服务器没有响应。
var xhr=new XMLHttpRequest();
xhr.open("GET","http://localhost:8081/test.php",true);
xhr.timeout = 1000;
xhr.send();
xhr.ontimeout = function () {
console.log('您的请求超时了');
};
abort()方法用于终止已经发出的请求,readyState 属性将被置为0
XMLHttpRequest.upload 属性返回一个 XMLHttpRequestUpload对象,用来表示上传的进度。
onreadystatechange属性存储一个函数,每当 readyState 属性改变时,就会调用该函数。所以也叫onreadystatechange事件。可以为onreadystatechange事件绑定处理函数或者监听事件。
var xhr=new XMLHttpRequest();
//readyState值的变化会触发onreadystatechange事件
xhr.onreadystatechange = function(){
console.log('readyState属性:',xhr.readyState)
};
xhr.open("GET","https://www.fastmock.site/mock/d6b39fde63cbe98a4f2fb92ff5b25a6d/api/getOne?id=1",true);
xhr.send();
//请求成功时触发,此时readystate为4
xhr.onload=function(){
console.log('onload事件:readyState属性:',xhr.readyState)
};
和onreadystatechange属性相似的还有事件:
事件 | 触发条件 |
---|---|
onreadystatechange | readyState改变时触发;但readyState由非0值变为0时不触发。 |
onloadstart | 开始发出请求时触发(end()方法后立即触发) |
onprogress | 服务器已经响应,等待处理时周期性地触发 |
onload | 当请求成功时触发,此时readystate为4 |
onloadend | 当请求结束(包括请求成功、失败、终止)时触发 |
onabort | 终止请求时触发,如调用xhr.abort()后触发 |
ontimeout | xhr.timeout不等于0,由请求开始即onloadstart开始算起,当到达xhr.timeout所设置时间请求还未结束即onloadend,则触发此事件。 |
onerror | 请求错误时触发;在请求过程中,若发生Network error则会触发此事件(若发生Network error时,上传还没有结束,则会先触发 |
XMLHttpRequest对象详解
XMLHttpRequest Level 2定义了 FormData 类型用以将表单数据序列化,以便用XMLHttpRequest来发送数据。
通过上图可以看到FormData对象没有实例属性,可以用的都是一些原型方法,一般使用FormData对象有两种方式:
var formData = new FormData()
formData.append("userName", 'admin')
formData.append("familyMembers", 'mother')
formData.append("familyMembers", 'father')
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:5002')
xhr.onload = function () {
console.log(JSON.parse(xhr.response))
}
xhr.send(formData)
<form method="post" id="myForm">
<input type="text" name="userName" >
<input type="checkbox" name="familyMembers" value="mother" >mother
<input type="checkbox" name="familyMembers" value="father" >father
<input type="button" value="提交" id="btn">
form>
<script>
var myForm = document.getElementById('myForm')
var btn = document.getElementById('btn')
btn.onclick = function () {
var formData = new FormData(myForm)
formData.get('userName')
formData.get('familyMembers')
const xhr = new XMLHttpRequest()
xhr.open('POST', 'http://localhost:5002')
xhr.onload = function () {
console.log(JSON.parse(xhr.response))
}
xhr.send(formData)
}
script>
FormData对象详解
要实现上传文件,需要一个文件域 file
[](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/Input/file)元素详解使用户可以选择一个或多个元素以提交表单的方式上传到服务器上。
<input type="file" id="files"/>
<script>
var input = document.getElementById('files')
input.addEventListener('change', function () {
const formData = new FormData()
formData.append('id', 1)
formData.append('file', input.files[0])
const xhr = new XMLHttpRequest()
// 要在open方法前添加事件监听
xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
var percentComplete = Math.round(event.loaded / event.total * 100)
console.log(new Date().getTime(), percentComplete + '%')
}
}
xhr.open('POST', 'http://localhost:5002/upload')
xhr.onload = function () {
console.log(JSON.parse(xhr.response))
}
xhr.send(formData)
})
script>
upload 属性,返回一个 XMLHttpRequestUpload对象,用来表示上传的进度。
事件 | 相应属性的信息类型 |
---|---|
onloadstart | 获取开始 |
onprogress | 在上传阶段(即xhr.send()之后周期性触发 |
onabort | 获取操作终止 |
onerror | 获取失败 |
onload | 获取成功 |
ontimeout | 获取操作在用户规定的时间内未完成 |
onloadend | 获取完成(不论成功与否) |
XMLHttpRequest Level 2可以取回二进制数据,常用于下载文件,接收二进制数据时,需要设置配置XHR属性responseType为“blob”或“arraybuffer”。
下面的两个例子分别用这两个方法从服务器请求一个二进制的数据并下载到本地,涉及到的Blob对象和
ArrayBuffer对象可以参考Javascript操作文件
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:5002/download')
xhr.responseType = 'blob'
xhr.onload = function () {
console.log(xhr)
var aEle = document.createElement('a')
aEle.download = 'demo'
aEle.href = URL.createObjectURL(xhr.response)
aEle.click()
}
xhr.send()
在浏览器Network面板中可以看到返回的如下二进制数据var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:5002/download')
xhr.responseType = 'arraybuffer'
xhr.onload = function () {
console.log(xhr)
var binary = ''
var buffer = xhr.response
var bytes = new Uint8Array(buffer)
for (var i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i])
}
var src = window.btoa(binary)
var aEle = document.createElement('a')
aEle.download = 'demo'
aEle.href = 'data:image:png,base64' + src
aEle.click()
}
xhr.send()