​Ajax​​用一句话来说就是无须刷新页面即可从服务器取得数据。注意,虽然​​Ajax​​翻译过来叫异步​​JavaScript​​与​​XML​​,但是获得的数据不一定是​​XML​​数据,现在服务器端返回的都是​​JSON​​格式的文件。

完整的​​Ajax​​请求过程

完整的​​Ajax​​请求过程

  1. 创建​​XMLHttpRequest​​实例
  2. 发出​​HTTP​​请求
  3. 接收服务器传回的数据
  4. 更新网页数据

下面先看一个红宝书上给出的发起​​Ajax​​请求的例子,​​API​​的用法在后面章节给出。


var xhr = new XMLHttpRequest(); // 创建XMLHttpRequest实例

xhr.onreadystatechange = function(){
if (xhr.readyState == 4){ // 判断请求响应过程阶段,4 阶段代表已接收到数据
if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码
console.log(xhr.responseText); // 输出响应的文本
} else {
console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码
}
}
};

xhr.open('get', 'example.txt', true); // 初始化xhr实例,或者说启动请求
xhr.send(null); // 设置HTTP请求携带参数,null为不带参数


​ Ajax​​请求过程详解

1. 创建​​XMLHttpRequest​​实例

从上面的的代码可以看出,创建一个​​XHR​​实例方式为:


var xhr = new XMLHttpRequest();


2. 发出HTTP请求

实例创建好后,首先需要启动一个​​HTTP​​请求,使用​​XHR​​的​​open()​​方法,​​open​​方法接受三个参数


XMLHttpRequest.open(method, url, isAsync)
// 例如
xhr.open('get', 'http://www.baidu.com', true)


第一个参数为​​http​​请求使用方法,如('get','post'等),第二是参数是请求的​​url​​, 第三个参数代表是否异步发送请求(可选)。调用​​open()​​方法后会启动一个​​http​​请求,但它不会立即发送请求,处于待命状态。需要注意的是:请求的​​url​​必须要跟请求源域(origin)同域,也就是说协议、域名、端口号要一致,跨域请求要使用别的方法。接着调用​​send()​​方法就会发出这个​​http​​请求。


xhr.open('get', 'http://www.baidu.com', true)
xhr.send(null)


​send()​​方法接受一个参数,为​​http​​请求发送的数据(通常用于'post'方法),如果为​​null​​,表示不发送数据。至此,一个异步的​​http​​请求就发送到了服务器。

3. 接收服务器传回的数据

3.1 发送同步请求

如果将​​open​​方法的第三个参数设为​​false​​,即为同步请求,当收到服务器的响应后,相应的数据会自动填充到​​XHR​​对象的属性中,主要包括以下四个:

  • ​responseText​​:作为响应主体被返回的文本。
  • ​responseXML​​: 响应返回的​​XML​​文档,能接收到的前提是,响应的​​Content-Type​​字段的值为
    ​text/xml​​或者​​application/xml​​。
  • ​status​​: ​​HTTP​​状态码。
  • ​statusText​​: ​​HTTP​​状态码说明。

当客户端收到以上信息后,首先要判断​​HTTP​​状态码来确认响应是否成功,状态码在200-300之间表示请求成功,同时304代表请求资源未被修改,可使用浏览器本地缓存。如果成功就可以获取响应报文主体中的数据了。


xhr.open('get', 'http://www.baidu.com', false)
xhr.send(null)

if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码
console.log(xhr.responseText); // 输出响应的文本
} else {
console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码
}


3.2 发送异步请求

如果将​​open​​方法的第三个参数设为​​true​​,即为异步请求。那么就需要一个事件来通知程序异步请求的结果是否返回。​​XHR​​对象中的​​readyState​​属性,表示请求/响应整个过程所处的阶段,它有五个值分为对应五个阶段:

  • 0:未初始化。未调用​​open()​​方法。
  • 1:启动。已经调用​​open()​​方法,但未调用​​send()​​方法。
  • 2:发送。已调用​​send()​​方法,但未收到响应。
  • 3: 接收。已经接收到部分响应数据。
  • 4:完成。已经接受到全部响应数据。

​readyState​​的值每变化一次,都会触发一次​​readStatechange​​事件,我们定义一个事件处理函数​​onreadStatechange()​​,并监听​​readyState == 4​​状态,就可以得知响应数据已全部收到,并进行下一步操作。那么就是文章开头给出的代码:


var xhr = new XMLHttpRequest(); // 创建XMLHttpRequest实例

xhr.onreadystatechange = function(){
if (xhr.readyState == 4){ // 判断请求响应过程阶段,4 阶段代表已接收到数据
if (xhr.status >=200 && xhr.status < 300 || xhr.status == 304) { // 校验HTTP状态码
console.log(xhr.responseText); // 输出响应的文本
} else {
console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码
}
}
};

xhr.open('get', 'example.txt', true); // 初始化xhr实例,或者说启动请求
xhr.send(null); // 设置HTTP请求携带参数,null为不带参数


补充XHR中三个有用的事件

​timeout​​事件

当超出了设置时间还未收到响应,就会触发​​timeout​​事件,进而调用​​ontimeout​​事件处理程序。同时​​timeout​​也是​​XHR​​的一个属性,用于设置这个时间阈值。下面是用法:


xhr.ontimeout = function() {
alert('timeout!')
}

xhr.open('get', 'http://www.baidu.com', true)
xhr.timeout = 1000 // 时间阈值设为1秒
xhr.send(null)


​load​​事件

​load​​事件用于简化对​​readState​​值的判断,响应数据全部接收完毕后(也就是​​readState == 4​​)会触发​​load​​事件,使用​​onload​​事件处理函数进行后续操作,​​onload​​会接收一个​​event​​对象,它的​​target​​属性等于​​XHR​​对象,当然我们在定义这个事件处理函数时也可以不传入这个参数,来看下面的用法:


var xhr = new XMLHttpRequest()
xhr.onload = function () {
if(xhr.status >=200 && xhr.status < 300 || xhr.status == 304) {
console.log(xhr.responseText); // 输出响应的文本
} else {
console.error(xhr.status, xhr.statusText); // 打印其他HTTP状态码
}
}
xhr.open('get', 'http://www.baidu.com', true)
xhr.send(null)


这样就不用去关心​​readyState​​值的变化情况了。当然如果想在特定​​readyState​​值上做一些逻辑处理,还是要用之前的方法。

​progress​​事件

这个是很有用的一个事件,​​progress​​事件会在浏览器接收数据期间周期触发,代表整个请求过程的进度,它的事件处理程序​​onprogress​​接收一个​​event​​对象,​​event.target​​是​​XHR​​对象,另外​​event​​还有三个属性:

  • ​lengthComputable​​:Boolean值,进度信息是否可用。
  • ​position​​:已经接收到的字节数。
  • ​totalSize​​:总共要接收的字节数,被定义在响应报文的​​Content-Length​​字段中。

如果响应报文中有​​Content-Length​​字段,那么我们就可以计算当前时刻响应数据的加载进度了,这也是之前看到的一个面试题。看下面的代码:



xhr.onprogress = function(event) {
if(event.lengthComputable) {
console.log(`Received: ${(event.position/event.totalSize).toFixed(4)*100}%`);
}
}


其他还有很多有用的API,如​​FormData​​表单序列化,​​overrideMimeType()​​重写​​XHR​​响应的​​MIME​​类型等等,后面慢慢更新。