Ajax的全称就是Asynchronous JavaScript + XML,它的本意是为了可以在不刷新页面的情况下异步更新数据。
而实现这一功能的核心就是XMLHttpRequest(XHR),它能够获取新数据,并通过DOM操作将新数据插入到页面当中。
虽然它的名称中包含有XML,但其实Ajax通信与数据格式没有关系,Ajax可以返回HTML、XML、json、jsonp、text、script等数据类型。
Ajax的一个基本流程
在IE7版本之后,以及Firefox、Opera、Chrome、Safari浏览器中,我们可以直接使用XMLHttpRequest构造函数来创建最新的XML对象。
var xhr = new XMLHttpRequest();
在使用XML对象的第一步是使用open方法,open接受3个参数:
要发送请求的类型("get"、"post"等)、请求的地址以及是否异步发送请求的布尔值。
xhr.open("get","example.php",false)
其中的第二个参数必须是在同一个域中使用相同端口和协议的地址,否则会引发安全错误。
在这之后调用xhr.send()方法,如果是POST请求的话,send里就需要放入要传的信息
在收到服务器的响应之后,JavaScript会自动填充XHR对象的属性,常用到的属性有以下几个:
- responseText:响应的文本
- status:响应的HTTP状态
- statusText:HTTP状态的说明
首先应该做的是检查status属性,如果是200的话,那么就说明本次响应已经成功了,或者是304的话,说明本地浏览器有缓存,可以直接调用。
同时我们一般是使用异步发送请求,所以我们还得检测XHR对象的readyState属性,该属性表示请求/响应过程的当前阶段。
- 0:尚未调用open()方法
- 1:启动open()方法,但还没启动send()
- 2:调用send(),但还未收到响应
- 3:已经接收到部分信息
- 4:已经收到全部信息
每一个阶段的改变都会触发一次onreadystatechange事件。
所以一个基本的Ajax流程的例子如下所示:
var xhr = new XMLHttpRequest();
xhr.open("get","example.php",false);
xhr.send();
xhr.onreadystatechange = function (){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300)) || xhr.status == 304){
alert(xhr.responseText)
}else {
alert(xhr.status)
}
}
}
XMLHttpRequest2级
XHR1级已经把Ajax的实现细节都给描述出来了,在此基础上,XHR2级对整个XHR进行了升级进化。
主要有以下几个方面:
- FormData:
用于序列化表单以及创建与表单格式相同的数据,使用new创建并且直接通过send函数发送。
创建时可以直接传入键值对
var data = new FormData();
data.append("name", "Tuncan")
或者预先传入表单元素
var data = new FromData(document.forms[0])
- 超时设定
XHR对象新增了一个timeout属性,表示请求在多少时间之后便会终止,在给定时间到达之后,还没有接到响应便会触发timeout事件并调用omtimeout事件处理程序。
xhr.timeout = 1000;
xhr.ontimeout = function (){
alert("Request did not return in a second")
}
- overrideMimeType
用于重写XHR的MIME类型,该类型决定XHR对象如何处理返回的响应,例如如果服务器返回的MIME类型是text/plain,但里面的文档类型是XML,根据MIME类型XHR的responseXML是null,所以需要overrideMimeType在send调用前重写MIME类型为XML才能处理。
var xhr = new XMLHttpRequest();
xhr.open("get","text.php",true);
xhr.overrideMimeType("text/xml");
xhr.send(null);
XMLHttpRequest的进度事件
在刚刚使用XHR实现Ajax的流程当中,可以发现整个响应会分为不同的阶段,在W3C里为其定义为了6个进度事件:
loadstart:接收到响应数据的第一个字节时触发
progress:接受响应期间不断地触发
error:请求错误时触发
abort:调用abort()方法终止连接时触发
load:接收到完整响应时触发
loadend:通信完成或者触发error、abort或load事件时触发
每一个请求都是以loadstart开始,经历一个或多个progress,然后接到error、abort或是load中的一个,最后以loadened结束。
其中的load事件和progress事件有一些细节需要注意:
- load事件
Firefox曾致力于简化异步交互模型,所以引入load事件来代替readystatechange事件,响应接受完毕之后才会触发load事件,因此也没有必要去检查readyState属性了。
xhr.onload = function(){
if ((xhr.status >= 200 && xhr.status < 300)) || xhr.status == 304){
alert(xhr.responseText)
}else {
alert(xhr.status)
}
}
- progress事件
progress事件用以表示当前接收数据的进度,它会被周期性地触发,每次触发都会返回一个event对象,event对象的target属性是XHR对象
同时它还含有三个属性:
lengthComputable:进度信息是否可用
position:表示已接收的字节数
totalSize:表示响应头部的预期字节数
xhr.onprogress = function (event){
var status = document.getElementById("status")
if (event.lengthComputable){
status.innerHTML = "Received" + event.positon + "of" + event.totalSize
}
}
需要注意的是要在xhr.open()之前定义好该事件
跨域资源共享——突破XHR的限制
因为在默认情况下,页面通过XHR只能访问跟它处在同一个域中的资源,这是来源于跨域安全策略。
但是在实际额开发过车个当中,我们一定会遇上需要进行跨域请求的地方,那么这时候就需要用上CORS了
CORS(Cross-Orign Resource Sharing,跨域资源共享),其基本思想就是使用自定义的Http头部实现浏览器与服务器之间的沟通,从而确定请求是否成功。
例如,对于一个请求,在头部里加入Origin
Origin: http://www.nczonline.net
如果服务器认为该请求可以接受,就在Access-Control-Allow-Origin头部中回发相同的源信息
Access-Control-Allow-Origin: http://www.nczonline.net
这样就完成了一次CORS。
跨域资源共享在非IE浏览器中,如Firefox3.5+,Safari 4+,Chrome,iOS版Safari和Android的WebKit中,都通过XMLHttpRequest实现了对CORS的原生支持。处于安全考虑,加上了如下的限制:
- 不能使用自定义头部
- 不能发送和接受Cookie
- getAllResponseHeaders()总会返回空字符串
在IE浏览器当中,主要是通过引入XDR对象,创建时通过new XDomainRequest穿件即可
该对象与XHR最重要的不同之处就在于不能使用Cookie,只支持GET和POST请求,以及只支持异步请求
如果开发一定要使用Cookie的话,可以将withCredentials属性设置为true,如果服务器接受该请求,就会使用Access-Control-Allow-Credentials: true来响应
最后如果想要实现跨浏览器的CORS时,通过检查XHR中是否带有withCredentials属性,再结合检查XDomainRequest是否存在,就可以兼顾所有浏览器了。
function createCORSRequest (method,url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method,url,true);
}else if ( typeof XDomainRequest != "undefined"){
xhr = new XDomainRequest();
xhr.open(method,url)
}else {
xhr = null
}
return xhr
}
其它的跨域方式
虽然说现在CORS已经无处不在,但在其发明出来之前,要想实现跨域请求还可以通过DOM中一些自带的功能,毕竟这样可以省去修改服务器代码的时间。
- 图像Ping
通过标签可以直接加载其它网站上的图片,而不用担心跨不跨域的问题,它自己带有onload和onerror事件,可以用于处理响应成功和失败事件。
var img = new Image()
img.src = "https://pic4.zhimg.com/v2-de96b4afbe2140ace8c268bb60112283_b.jpg"
但是这种方法有两个缺点,一是只能使用GET请求,二是无法访问服务器的文本
- JSONP
JSONP是JSON with padding的简写,它是通过动态调用