引言
作为一名WEB开发者,我想Ajax技术是一定需要掌握的。你也许平时没有使用JavaScript真正的写过Ajax。但是你一定使用过JQuery里面的相关函数来进行异步调用。今天我们就来介绍下原生Ajax的技术。
XMLHttpRequest对象
说到原生的Ajax技术实现,XMLHttpRequest对象是肯定是需要介绍的。历史上IE5是第一个支持XHR的浏览器,在IE5中,XHR对象是通过MSXML库中的一个ActiveX对象来实现的。因此在IE5,IE6中可能会遇到3种不同版本的XHR对象。为了在低版本IE中可以稳定的创建XHR。我们可以使用以下函数来创建。请看代码:
/**
* 通过XMLHttpRequest来了解Ajax技术的本质.
* 作为约定俗成的准则,JS中构造函数以大写字母开头
**/
function AjaxInstance() {
this.xmlhttprequest = function () {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
//适用于IE7之前的版本
} else if (typeof ActiveXObject !== "undefined") {
if (typeof arguments.callee.activeXString !== "string") {
var versions = ["MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP"];
for (var i = 0, len = versions.length; i < len; i++) {
try {
arguments.callee.activeXString = versions[i];
return new ActiveXObject(versions[i]);
} catch (e) { }
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
else {
alert("您的浏览器版本不支持Ajax技术,请升级新版本浏览器...");
}
}
}
在例子中我们看到,在IE7之前的版本中,创建XHR对象,需要3个库。并且早期IE中XHR对象实质是ActiveXObject对象。
注意:IE7+,Chrome,Firefox,Opera,Safiri都是原生支持XMLHttpRequest对象的。所以在这些浏览器中只需要像第4行代码一样new一个对象即可。
XHR的用法
在使用XHR对象时,要调用的第一个方法是open()。该方法接收3个参数,它们分别是:要发送的请求的类型(get,post)、请求的URL以及表示是否异步发送请求的布尔值。例如:
this.xmlhttprequest.open(method, url, true);
有两点需要说明:1、URL是相对于执行代码的当前页面。2、调用open方法并不会立即发送请求,而是启动一个请求以备发送。
注意:只能向同一个域中使用相同端口和协议的URL发送请求。如果URL与启动请求的页面有任何的差异,都会引起安全错误。
呀发送特定的请求,还必须调用send方法。send方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,必须将参数设置为null,因为这个参数对于有些浏览器来说是必须的。调用send方法后,请求就会被发送到服务器。
在收到服务端的响应后,响应的数据会自动填充XHR对象的相关属性。详细情况如下:
responseText:作为响应主体被返回的文本。
responseXML:如果响应的内容类型是"text/xml或者application/xml",这个属性中将保存着响应数据的XML DOM文档。
status:响应的HTTP状态。
statusText:HTTP状态的说明。
在接收到响应后,第一步操作一般都是验证status属性,以确定响应是否已经成功返回。一般都将200作为成功的标志。这时候,responseText属性的内容已经就绪。此外,状态代码为304表示请求的资源被没有被修改。可以直接使用浏览器中缓存的版本。为确保收到适当的响应。我们应该像下面这样检查这两种状态。例如:
//响应有效
if ((tempxmlhttp.status >= 200 && tempxmlhttp.status < 300) || tempxmlhttp.status == 304) {
callback(tempxmlhttp.responseText, tempxmlhttp.responseXML);
} else {
failback(tempxmlhttp.status, tempxmlhttp.statusText);
}
在大多数情况下,我们都会使用XHR进行异步访问。这样可以让JavaScript继续执行,而不必为了等待服务端的响应而阻塞代码的执行。我们可以通过XHR的readyState来获取请求/响应过程中的状态。详细的信息如下:
0---未初始化,尚未调用open方法。
1---启动,已经调用open方法,尚未调用send方法。
2---发送,已经调用send方法,但是还没有收到服务端响应。
3---接收,已经接收到部分响应数据。
4---完成,已经接收到全部响应数据,可以在客户端使用了。
只要readyState属性的值由一个值变成另一个值,都会触发readystatechange事件。可以使用这个事件来监测每次状态变化以后的readyState的值。必须在调用open函数之前指定onreadystatechange事件处理程序才能保证跨浏览器兼容性。例子如下:
this.xmlhttprequest.onreadystatechange = function () {
if (tempxmlhttp.readyState == 4) {
//响应有效
if ((tempxmlhttp.status >= 200 && tempxmlhttp.status < 300) || tempxmlhttp.status == 304) {
callback(tempxmlhttp.responseText, tempxmlhttp.responseXML);
} else {
failback(tempxmlhttp.status, tempxmlhttp.statusText);
}
}
}
this.xmlhttprequest.open(method, url, true);
this.xmlhttprequest.send(null);
HTTP头部信息
每一个Http请求和响应都会带有相应的头部信息。XHR对象也提供相应的方法来操作请求和响应的头部信息。下面我们就来看看XHR在发送请求时。默认携带的Http头部信息。如图:
使用setRequestHeader方法可以设置请求头的信息,这个方法接收两个参数,头部字段的名称和头部字段的值。必须在调用open方法之后,send方法之前调用setRequestHeader方法。
GET请求
GET请求是最常用的的请求类型,一般用于向服务器查询某些信息。必要时,可以将查询字符串参数追加到URL尾部,以便将参数发送到服务器。对于XHR而言,对于传入open方法的URL尾部的查询字符串必须经过正确的编码才行。使用GET请求的URL应该是这样:/Home/Index?Id=1&type=book(参数没有编码)。
我们可以使用一个参数来构造发送给服务端的查询字符串。例如:
/**
* 将JavaScript中的对象解析成查询字符串(key=value&key1=value1的形式)
**/
function appendParameters(data) {
var query = "";
//枚举JS对象的属性
for (var item in data) {
query += (encodeURIComponent(item) + "=" + encodeURIComponent(data[item]));
query += "&";
}
query = query.substr(0, query.length - 1);
return query;
}
我们可以使用下面的代码来使用XHR来进行GET请求。如下:
url += (url.indexOf("?") == -1) ? "?" : "&";
url += query;
//解决缓存的转换
url += "&t=" + (new Date()).valueOf();
this.xmlhttprequest.open(method, url, true);
this.xmlhttprequest.send(null);
POST请求
POST请求是使用频率仅次于GET的请求类型,一般用于向服务器发送应该被保存的数据。POST请求会将数据作为请求的主体进行提交,而GET请求传统上不是这样的。POST请求的主体可以包含非常多的数据,并且格式不限。
我们可以通过使用XHR来模拟表单的提交行为。需要将Content-Type设置为application/x-www-form-urlencoded也就是表单提交时的内容类型。同时我们需要将数据传递到服务器时,需要使用XHR的send方法来传递数据。使用XHR进行POST调用的例子如下:
//如果是POST方式,需要设置请求头
if (method.toLowerCase() === "post") {
this.xmlhttprequest.open(method, url, true);
this.xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//post传递数据时需要(query是key=value&key1=value1的形式的字符串)
this.xmlhttprequest.send(query);
}
注意:与GET请求相比,POST请求消耗的资源会更多一些。从性能的角度看,发送相同的数据量,GET请求的数据最多可以达到POST请求的两倍。
完整的Ajax的例子(已封装)
/**
* 通过XMLHttpRequest来了解Ajax技术的本质.
* 作为约定俗成的准则,JS中构造函数以大写字母开头
**/
function AjaxInstance() {
this.xmlhttprequest = function () {
if (typeof XMLHttpRequest !== "undefined") {
return new XMLHttpRequest();
//适用于IE7之前的版本
} else if (typeof ActiveXObject !== "undefined") {
if (typeof arguments.callee.activeXString !== "string") {
var versions = ["MSXML2.XMLHTTP.6.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP"];
for (var i = 0, len = versions.length; i < len; i++) {
try {
arguments.callee.activeXString = versions[i];
return new ActiveXObject(versions[i]);
} catch (e) { }
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
else {
alert("您的浏览器版本不支持Ajax技术,请升级新版本浏览器...");
}
}
} /**
* 使用XMLHttpRequest对象调用服务端方法
* method:调用ajax的方式:get/post
* url:调用服务端方法的路径(asmx,controller都可).如:Login/CheckUser注意get,post时不要使用?Id=XX&Name=XX等形式.
* data:调用方法时传递的参数.只需要以对象形式{Id:"",Name:""}传递data参数即可,无参传递null
* callback:获取结果的回调函数,会传递结果给回调函数.客户端调用时只需定义具有两个参数的函数即可.如:function callback(responseText,responseXml){}
* failback:出错回调,客户端调用时只需定义具有两个参数的函数即可.如:failback(errorCode,errorMsg),调用该函数时,会将出错Http状态码,出错信息发送给相关参数
**/
AjaxInstance.prototype.call = function (method, url, data, callback, failback) {
//首先判断调用者有无定义相关回调函数
if (!(callback instanceof Function) && !(failback instanceof Function)) {
alert("调用call方法必须指定callback和failback方法");
return;
}
//此处需将this变量赋值给临时变量,若使用this.xmlhttprequest会出现undefined.在function内部使用this指的是window对象.这是JS缺陷
var tempxmlhttp = this.xmlhttprequest;
this.xmlhttprequest.onreadystatechange = function () {
if (tempxmlhttp.readyState == 4) {
//响应有效
if ((tempxmlhttp.status >= 200 && tempxmlhttp.status < 300) || tempxmlhttp.status == 304) {
callback(tempxmlhttp.responseText, tempxmlhttp.responseXML);
} else {
failback(tempxmlhttp.status, tempxmlhttp.statusText);
}
}
} var query = null;
if (data !== null && (data instanceof Object)) {
query = appendParameters(data);
}
//如果是POST方式,需要设置请求头
if (method.toLowerCase() === "post") {
this.xmlhttprequest.open(method, url, true);
this.xmlhttprequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//post传递数据时需要(query是key=value&key1=value1的形式的字符串)
this.xmlhttprequest.send(query);
}
else {
url += (url.indexOf("?") == -1) ? "?" : "&";
url += query;
//解决缓存的转换
url += "&t=" + (new Date()).valueOf();
this.xmlhttprequest.open(method, url, true);
this.xmlhttprequest.send(null);
}
}; /**
* 将JavaScript中的对象解析成查询字符串(key=value&key1=value1的形式)
**/
function appendParameters(data) {
var query = "";
//枚举JS对象的属性
for (var item in data) {
query += (encodeURIComponent(item) + "=" + encodeURIComponent(data[item]));
query += "&";
}
query = query.substr(0, query.length - 1);
return query;
}