AJAX是Asynchronous JavaScript And XML的简写,即异步javascript和XML。它是一种可以向服务器请求数据而不需要网页刷新的技术。
早期前后端数据交互使用'客户端发出请求-> 服务端接收请求并返回相应的html文档->页面刷新,客户端加载新的html文档'这样一种模式,属于服务器渲染时代。我们可以看出这种模式给服务器压力极大而且它实现的是同步刷新对于网速也有一定要求。如果我们只是对页面中某一部分数据进行修改而服务器将要重绘整个页面再返回给浏览器加载,这会产生大量不必要的开销,AJAX技术的出现正好解决这一问题,在页面数据进行变动时,只向服务器请求新数据,并且在阻止页面刷新的情况下动态替换页面中的数据进行局部刷新。这里的异步javascript不是指的以后ajax请求只能采取异步,而是代表浏览器的局部刷新。
XML(extensible markup language),是一种类似于HTML用来存储数据的可拓展标记语言。早期客户端和服务器之间数据通信都是基于这咱格式的数据,而现在使用了一种更加容易映射到一般语言的数据结
构JSON,它更加的轻量,简洁和清晰。XML和JSON各用其适用场景,JSON并不是XML的替代品。
AJAX包括以下几个步骤:1、创建AJAX对象 2、发出HTTP请求 3、接收服务器传回的数据 4、更新网页数据。概括起来说,AJAX就是通过原生的XMLHttpRequest对象发出HTTP请求,得到服务器返回的
数据后,再进行局部刷新。
一、AJAX基础
ajax的核心技术就是XMLHttpRequest对象(简称XML)。最先由微软提出,逐渐被其它浏览器厂商所认同。它是浏览器提供的一个API,可以用来向服务器发送请求并解析服务器的响应,在这个过程中浏览器不
会被刷新。它本质上只是浏览器提供的一个构造函数而已。
1、创建ajax对象
我们创建一个XML对象的实例可以看到里面包含的属性和方法:
// 发送ajax请求
const xhr = new XMLHttpRequest();
xhr.open('GET', 'example.php', false);
xhr.sendRequestHeader('myHeader', 'myHeader');
xhr.send();
console.dir(xhr);
//我们可以看到主要属性和方法有:
// xhr.open(): 打开一个url请求
// xhr.abort(): 中断请求
// xhr.send(): 发送请求
// xhr.getAllResponseHeaders(): 获取所有响应头信息
// xhr.getResponseHeaders([key]): 获取指定响应头信息
// xhr.overrideMimeType(): 覆盖服务器返回的MIME类型
// xhr.setRequestHeader(): 设置请求头
// 属性
// xhr.response / xhr.responseText / xhr.responseXML: 响应 / 响应的文本数据 / 响应的xml数据
// readyState: xhr状态码
// xhr.status / shr.statusText: http状态码 / http状态码说明文本
// upload: 上传对象
// 另外浏览器还提供了一个onreadystatechange,用来监听xml实例属性的变化
2、发送http请求
在使用xhr对象时,我们使用open()方法来发送请求,这一个请求可以接受三个参数,即请求方式,URL地址,是否异步发送请求的布尔值,如下所示:
xhr.open('GET', 'example.php', false);
open()方法第一个参数是用于指定发送请求的方式,这是一个字符串,不区分大小写,但一般使用大写形式。其中最主要的方式有'GET'和‘POST’。GET及POST区别在URL解析过程这一篇文章中有具体详细的介绍。
这里需要注意如果使用的是相对路径,那么请求url是相对于执行代码的当前页面。
GET: xhr.open('GET', 'http://127.0.0.1:8888/user/list?ls=one&from==随机数');
xhr.send(null);
POST: xhr.open('POST', 'http://127.0.0.1:8888/user/');
xhr.send('list?ls=1&from=wx');
设置请求头,每一个http请求和响应都有相应的头部信息,包含数据,收发者网络环境和状态等等。XMLHttpRequest对象提供.setRequestHeader()方法用来操作请求头的头部信息。默认情况下当发送ajax请求时会附带许多信息如:Accept,Cookie,Host,Referer......
send()方法接收一个参数,作为请求主体传递给服务器的信息,调用send()方法后,请求被分派到服务器。如果是GET请求的话,send()方法参数为null,如果是POST方法,send的参数是要传递给服务器的信息。
它常用的有几种数据格式,对应着请求头中相应的Content-Type.
raw(原始格式): text/json/xml/html MIME:text/plain;application/json;application/xml;text/html
form-data: 一般是用于表提交,文件的上传 MIME:multipart/form-data;
x-www-form-urlencoded: 它可以把要上传的信息解析成xxx=xxx&xxx=xxx的格式。MIME:application/x-www-form-urlencoded
binary:二进制/字节流/文件流 MIME:根据选取文件类型,有自己相对应的MIME。如:image/png ;application/pdf......
let xhr = new XMLHttpRequest;
xhr.open('POST', 'http://127.0.0.1:8888/user/login');
//x-www-form-urlencoded格式 引入外部的Qs和md5文件
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(Qs.stringify({
name: 'xxxx',
password: md5('123456')
}));
//raw格式
xhr.sendRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
"name": "xxxx",
"age": 20
}));
// form-data格式
xhr.sendRequestHeader('Content-Type', 'multipart/form-data');
let formData = new FormData;
formData.append('name', 'xxxx');
formData.append('age', 20);
xhr.send(formData);
3、接收服务器传回的数据及更新
一个完整的http响应应该由状态码,响应头和响应主体组成。在收到响应后,这些都可以通过xhr对象的属性和方法使用。
response: response的属性是只读的,它代表了返回接收到的数据体,类型可以是Blob,ArrayBuffer,JSON或者是字符串,它是由XMLHttpRequestType属性的值决定的
respomseText: 它返回从服务器接收到的字符串,是一个只读属性。如果服务器返回的是josn,字符串或者是js都可以使用它,它是最常用的用于接收数据的属性。如果请求并没成功或者是数据不完整会返回null。
responseType: responseType属性是用于指定服务器返回的数据类型。
status: 响应状态码。状态码分为以下大类,不同类型代表不同的状态码
【1XX】: Informational(信息性状态码) 表示接收的请求正在处理即
【2XX】:Success(成功状态码) 表示请求正常处理完毕。如:200Ok(成功)/204No Content(服务器已经处理了请求,但不需要返回任何数据)/206Partial Content(服务器成功处理了部分get请求)
【3XX】:Redirection(重定向状态码) 表示需要进行附加操作以完成请求如:301Moved Permanently(永久转移)/304 Not Modified(未修改)
【4XX】 :Client Error(客户端错误状态码) 表示服务器无法处理请求,客户请求包括语法错误或者是不以正确执行。如: 400 Bad Request(请求参数有误)/404 Not Found/408 Request Timeout
【5XX】 :Server Error(服务器错误状态码) 表示服务器处理请求出错。如:500Internal Server Error / 505 Http Version Not Supported
请求可能是同步的,也可能是异步的。但如果接受的是同步响应,那么就需要将open()方法的第三个参数设置为false,send()方法将阻塞,直到请求完成,一旦send()返回,只需要检查xhr对象的status和
responseText属性就可。javascript是单线程的,当send()方法阻塞,它往往会导致整个浏览器UI冻结,如果连接的服务器响应较慢,那么浏览器将会冻结。如下所示:
let xhr = new XMLHttpRequest;
xhr.open('get', 'http://127.0.0.1:8888/user/login', false);
xhr.send(null);
//因为是同步响应,只有当服务器响应后才会执行下面代码,xhr.status不是默认值
if ((xhr.status >= 200 & str.status < 300) || xhr.status == 304) {
alert(xhr.responseText);
} else {
alert('Request was unsuccessful' + xhr.status);
}
4、onreadystatechange()
当请求为异步在xhr.send(null)语句执行以后,紧接着会执行下面的判断,而这时服务器尚未响应而我们注定会拿到一个默认的xhr.status值,这时我们也就不能获取到资源了,这时就需要用到XMLHttpRequest
实例的onreadystatechange方法对xhr.readyState属性进行监听。readyState表示请求/响应过程的当前活动阶段,可取下面的值:
0(UNSET): 未初始化。这个阶段确认了XMLHttpRequest对象已经存在,但还没有调用open()方法;
1(OPENED): 启动。已经调用了open方法根据参数(method,url,true)完成对象状态的设置。但还未调用send()方法,值为1时表示正在向服务端发送请求。
2(HEADERS_RECEIVED): 发送。已经调用send方法,接收到了服务器端的响应数据。但获得的还只是服务端响应的原始数据,并不能直接在客户端使用。
3(LOADING):接收。已经接收到了部分响应主体信息。根据服务器端响应头部返回的MIME类型把数据转换成能通过responseText或responseXML属性存取的格式,状态3表示正在解析数据。
4(DONE):完成。此阶段确认全部数据都已经解析为客户端可用的格式,可以通过XMLHttpRequest对象的相应属性取得数据在客户端进行使用。
只要readyState属性值由一个值变为另外一个值那么就会触发readystatechange事件。一般用于值为4阶段时,这时数据已经准备就绪。
readyState是状态值而status是状态码,二者是完全独立但又紧密联系的。readyState它表示ajax所经历的几种状态,无语是否成功都会响应,可以通过xhr.readyStatea获取,通过xhr.onreadystatechange进行监控;而status指的是状态码,无论ajax访问是否成功,HTTP协议根据所提交的信息,服务器返回Http头部信息代码通过xhr.status来获取。
let xhr = new XMLHttpRequest;
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
if ((xhr.status >= 200 & xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText);
} else {
alert('Request was unsuccessful' + xhr.status);
}
}
};
xhr.open('GET', 'http://127.0.0.1:8888/user/login', true);
xhr.send(null);
二、ajax事件
在前面提到了readystatechange方法,它可以用于检测http请求完成与否。在XHR2规范中,有提出了progress events规范,XMLHttpRequest对象在请求的不同阶段触发不同类型事件,故此不必再检查readyState属性了。主要有以下几类事件:
load是在接收到完整响应时触发,所以这时就没有必要去检查readystate属性了。一个完整请求也有可能是不成功的请求。
let xhr = new XMLHttpRequest;
xhr.onload = () => {
if ((xhr.status >= 200 & xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText);
} else {
alert('Request was unsuccessful!')
}
};
xhr.open('get', 'http://127.0.0.1:8888/user/login', true);
xhr.send(null);
progress事件是在浏览器接收到新数据期间周期性触发。onprogress事件处理程序会接收到event对象,其target是xhr对象。它有三个额外的属性即
lengthComputable: 表示进度信息是否可用的布尔值;
loaded: 表示目前接收到的字节数
total: 表示根据Content-Length响应头部确定的预期字节数;
有了这些个属性我们就可以实现加载进度条效果。
<div id='box'></div>
let xhr = new XMLHttpRequest;
xhr.onload = () => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
alert(xhr.responseText);
} else {
alert('Request was unsuccessful!')
}
}
xhr.onprogress = (event) => {
const boxEle = document.getElementById('box');
if (event.lengthComputable) {
boxEle.innerHTML = `Received ${event.loaded} of ${event.total} bytes.`
}
}
xhr.open('get', 'http://127.0.0.1:8888/user/info', true);
xhr.send(null);
如果请求超时会触发timeout事件;请求中断会触发abort事件,或者是请求发生错误时触发error事件。每一个请求都从触发loadstart事件开始,依次是progress,load,error,abort或者timeout事件中的一个,到最后的loadend。
// timeout属性:设置超时时间
// ontimeout方法:当响应时间超出timeout时触发
xhr.timeout = 2000;
var xhr = new XMLHttpRequest;
btn.onclick = () => {
xhr.abort();
}
xhr.ontimeout = () => {
alert('the request timed out!')
}