Ajax全面解析(来自JavaScript高级程序设计)

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的简写,它是通过动态调用

你可能感兴趣的:(ajax,xmlhttprequest)