关于Ajax的参考资料,现在网上一搜一大把,当前Web开发者经常接触的网页技术之一便是ajax我们日常的编码开发中不可避免的都在使用ajax,尤其是当前一些成熟而流行的前端JavaScript库(如jQuery,YUI,Prototype)通常都封装了ajax的接口并提供了实现,而web开发者只要按照类库的API说明,直接进行调用即可。大家都知道,JS类库之所以好用,是因为其实现按照自己的设计准则将日常Web前端开发涉及到的所有技术点都统一的进行抽象,封装,屏蔽一些诸如浏览器差异,跨平台的问题。这样便将诸多前端技术实现细节都进行了隐藏,而只暴露了前端开发中的那些常用接口。对于快速开发,我们直接使用类库即快捷又高效,但是作为学习,我个人觉得还是有必要对一些技术点进行简单的梳理总结,这有助于增加我们对技术的记忆和掌握的熟练度,同时也增加了我们对常用类库的设计理解。算是温故知新吧!
概念及简介:
Ajax,英文全拼Asynchronous JavaScript And XML(异步的Javascript和XML),是一种广泛使用与浏览器端的网页技术,主要用来提供创建快速和动态的网页。Ajax只是一个概念,在2005年由Jesse James Garrett在一篇名为《 Ajax : A New Approach to Web Applications 》的文章中首次提出并进行阐述。其中文章指出Ajax的定义:
- 利用XHTML+CSS作为展示层(standards-based presentation using XHTML and CSS;)
- 利用DOM动态展示和交互(dynamic display and interaction using the Document Object Model;)
- 利用XML+XSLT进行数据交互和操作(data interchange and manipulation using XML and XSLT;)
- 利用XMLHttpRequest进行异步数据获取(asynchronous data retrieval using XMLHttpRequest;)
- 利用Javascript将上述几项进行绑定(and JavaScript binding everything together.)
此外,文章也指出了传统的web应用交互模型和Ajax应用交互模型,并指出了二者的不同之处。如果示
可能大家对于此两种模式并不陌生,不过现在我们还是要简单的介绍一下
传统的Web应用允许用户填写表单(form),在表单完成之后就将被用户发送至服务器端,服务器接受表单数据并进行一定的逻辑处理,在服务器处理数据的这段时间,我们可以看到浏览器端的页面一直处于等待和空白的状态,之后服务器端在处理完数据之后,会返回一个新的页面至浏览器端,此时浏览器对返回的页面重新进行解析渲染,并最终展示给用户。这里我们可以看出:
- 在服务器处理表单数据的这段时间,用户浏览器端一直处于等待状态(此时页面可能是一个白板),造成了极差的用户体验;
- 服务器在处理完表单数据之后通常会重新发送一个新的页面,这浪费了宝贵的带宽资源(因为有些页面数据是不需要重复返回的);
- 整个的这种请求-->等待-->返回的响应模式,造成了时间的浪费。
相反,应用Ajax的Web应用,用户构建要提交到服务器端的数据(通过form等);异步发送至服务器端,用户页面为刷新,此时用户可以继续与页面进行交互;服务器处理数据,根据业务逻辑产生响应数据(格式可以是XML,亦或是纯文本);浏览器获取并解析返回数据,局部刷新用户页面。这里我们可以看出:
- 通过发送异步数据,浏览器端用户界面并没有出现等待情况,在数据发送和处理的过程,用户仍可以与页面进行交互(不会在面对白板页面);
- 服务器只是根据需要返回数据,有效的减少了数据量,节省带宽;
- 服务器端的一些业务逻辑可以在浏览器端完成,较少了服务器端的处理压力和时间;
- 整个的这种异步请求-->用户界面交互+服务器数据处理-->响应的模式,较传统的请求模式节省不少时间;
简单的代码示例:
下面,通过简单的代码来分析来熟悉一下ajax的整个处理流程,样例中的流程为:用户输入一个用户名,发送至服务器,之后由服务器返回一段文本,用户界面在收到该字符串后在页面中予以展现。
前台代码:request.html
03 |
<title>Simple Ajax Code</title> |
08 |
border:1px solid #ababab; |
17 |
<div id="box" class="box"> |
18 |
<label for="uname">用户名:</label> |
19 |
<input type="text" name="username" id="uname"> |
20 |
<button id="submitBtn">提交</button> |
23 |
var btn = document.getElementById("submitBtn"); |
24 |
var unameFile = document.getElementById("uname"); |
25 |
var box = document.getElementById("box") |
26 |
btn.onclick = function(){ |
28 |
var xhr = createXhr(); |
29 |
var uname = unameFile.value ; |
31 |
xhr.onreadystatechange = function(){ |
32 |
if(xhr.readyState == 4 && xhr.status == 200){ |
33 |
var resultServerText = xhr.responseText; |
34 |
var h2 = document.createElement("h2"); |
35 |
var textNode = document.createTextNode(resultServerText); |
36 |
h2.appendChild(textNode) |
40 |
xhr.open("POST" , "./receive.jsp" , true); |
41 |
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); |
42 |
xhr.send("uname="+uname); |
47 |
if(window.XMLHttpRequest){ |
48 |
return new XMLHttpRequest(); |
50 |
if(window.ActiveXObject){ |
52 |
return new ActiveXObject("MSXML2.XMLHTTP.3.0"); |
55 |
return new ActiveXObject("Microsoft.XMLHTTP"); |
后台代码:receive.jsp
2 |
String username = request.getParameter("uname"); |
3 |
String resText = "Hello "+username ; |
4 |
response.getWriter().print(resText); |
注意,当前两个页面在服务器相同的目录下。当程序启动后,在request.html的表单域中输入hmj并提交之后,会从后台取回数据并显示到页面上,如图示:
本例十分简单粗糙,但对于说明Ajax的处理流程也是足够的了。下面简单的解释下当用户输入用户名点击提交后发生的代码处理流程:
创建与服务器通讯的JavaScript对象 xhr(关于xhr对象,下文会有详细介绍),
1 |
xhr.onreadystatechange = function(){ .... } |
向xhr对象上添加一个回调函数,当Http响应成功后,会调用该函数。在该函数中我们通过
1 |
xhr.readyState == 4 && xhr.status == 200 |
来判断服务器响应是否成功,在确定响应成功后我们通过xhr.responseText,取回从服务器返回的数据,之后展现到页面上。本例中的返回结果只是普通的文本,如果返回结果类型为xml,你也可以通过xhr.responseXML属性,来获取xml对应的DOM对象。
通过
1 |
xhr.open("POST" , "./receive.jsp" , true); |
我们启动一个ajax请求,第一个参数为http请求的方法(本例中为post),第二个参数为请求的服务器地址,第三个参数标识请求是否为异步的(true标识异步),因为是利用post方法提交,在启动链接后我们还要通过
1 |
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); |
设置request的MIME类型,来模拟表单提交,这样在后台我们才能获取post中的参数
最后,通过调用
1 |
xhr.send("uname="+uname); |
我们发送数据到服务器端,其中send方法中传入的参数即为要提交到服务器的数据。
XMLHttpRequest简介
Ajax的核心为XMLHttpRequest。在浏览器脚本中XMLHttpRequest提供了一套API来完成与后台服务器端的数据交互,通过该API接口我们可以向服务器端发送HTTP或HTTPS请求并且获取从服务器端返回的数据。
XMLHttpRequest对象背后的概念首次由微软的Outlook Web Access开发者为Microsoft Exchange Server 2000创建。利用这个概念一个称为IXMLHttpRequest的接口在MSXML库的第二个版本中开发并实现。在1999年的3月份该版本的MSXML库被运用到IE5.0中,允许通过ActiveX控件,运用MSXML库中的XMLHTTP包装器来提供腿IXMLHttpRequest接口的访问。
Mozilla项目组在其Gecko排版引擎中开发并实现了一个名为nsIXMLHttpRequest的接口。该接口在功能上尽量模仿微软的IXMLHttpRequest接口。Mozilla通过一个其称之为XMLHttpRequest的JavaScript对象来包装使用该接口。XMLHttpRequest对象最早是在在2000年的12月发行的Gecko的0.6版本中可以访问,但是直到2002年6月5号的1.0版本的Gecko才提供了完全的功能。随之,XMLHttpRequest对象成为了在其它主流浏览器中成为事实上的标准,2004年的Safari1.2版本,2005年4月的Konqueror,Opera0.8版本都实现了该对象。
在2006年4月5日W3C组织发布了XMLHttpRequest对象的工作草案规范,意在为开发者提供一个XMLHttpRequest规范。最新的规范为2009年12月19号发布的XMLHttpRequest规范手册。
微软在2006年12月份发布的IE7下将XMLHttpRequest对象标识添加到其中。
2008年2月25号,W3C组织发布了另一个工作草案规范,称为“XMLHttpRequest Level 2”,提供了对XMLHttpRequest对象的一些扩展功能。包括但不仅限于:progress事件;跨站请求(cross-site requests);字节流处理。最新的“XMLHttpRequest Level 2”规范为2011年8月16日的一版。
2011年12月5日“XMLHttpRequest Level 2”被并入到XMLHttpRequest规范中,从此不再有XMLHttpRequest对象的第1或2版之分。
XMLHttpRequest属性:
readyState
说明:获取请求操作的当前状态
属性值:
类型:Integer
该属性对应的值包括以下几个状态选项
READYSTATE_UNSENT(0)
xhr对象创建,但是并未实例化(open方法还未调用)
READYSTATE_OPEND(1)
open方法已成功调用,但是send方法还未调用
READYSTATE_HEADERS_RECEIVED(2)
send方法已经调用,响应消息头和状态码可以获取,但是响应体还未获取
READYSTATE_LOADING(3)
数据下载中,responseText属性已经获取部分数据。响应体未全部获取
READYSTATE_DONE(4)
所有数据已获取完毕
每到达一个状态,都会触发xhr对象上的onreadystatechange上的回调函数
onreadystatechange
说明:xhr的回调函数,每次xhr的readyState属性发生变化时都会调用该函数,该回调发生在用户的UI线程上。在 回调中你可以根据自己的处理逻辑来编写流程,需要注意的是,在该回调函数中并未传递任何的参数
属性值:
类型:Function
responseText
说明:以纯文本形式表示的本次请求对应的响应数据结果,如果响应未成功或尚未设置则,返回null。该属性只读。
属性值:
类型:String
responseXML
说明:以XML Document Objecy Model(DOM)形式获取响应体,如果响应未成功或尚未设置或不能 以HTML或XML解析返回数据,返回null,浏览器会将返回结果按照text/xml流的形式进行解析,该属 性只读。
属性值:
类型:Object
status
说明:返回本次响应的HTTP状态码,此属性只读。有关HTTP状态码介绍,参见此
属性值:
类型:Integer
statusText
说明:返回本次http状态码对应的描述信息,此属性只读。
属性值:
类型:String
timeout
说明:get/set timeout的值,timeout表示浏览器等待服务器响应的最大毫秒数
属性值:
类型:Integer
XMLHttpRequest方法:
open()
说明:实例化一个XMLHttpRequest对象,该方法优先于任何提交请求的方法之前。
调用格式:
open(method , URL , async , user , password);
参数说明:
method
http请求方法类型,可选值为get/post/put/delete,该参数必须。
URL
http请求的提交路径信息,XMLHttpRequest对象不负责检测该URL格式的合法性,该属性必须
async
只是是否要发送异步请求。XMLHttpRequest对象支持两种类型的请求——异步和同步!如果为false那么send方法会等待response完全返回之后才会return;如果为true,则send方法调用完成后会立即返回,回调函数的调用会 以异步事件的形式调用。默认情况下该属性值为true。该属性可选。
user
服务器认证所提供的用户名,默认为空字符串,该属性可选。
password
服务器认证所提供的密码,默认为空字符串,该属性可选。
以上,我们看出,只有method,和URL两个参数在实例化XMLHttpRequest对象时是必须要设定的,另外如果没有显示的指定async属性的话,name我们发送的请求都将是异步的。注意:该方法并不会发送实际的请求。
setRequestHeader()
设置本次请求的HTTP请求头信息,该方法必须在open方法和send方法之间调用才生效。
调用形式:
setRequestHeader(header , value);
参数说明:
header:
http消息头名称
value:
http消息头对应的值
send()
向服务器提交请求,如果open的async属性为true(异步请求,默认为true),那么该方法调用完成之后会 立即返回;如果async为false(同步请求),那么该方法会等待服务器结果的返回,之后结束调用。
调用形式:
send()
该情况下send方法中不包含任何参数,所以不会向服务器提交任何额外参数。
send(data)
利用send方法,我们可以向服务器提交任何数据,参数中data必须满足一定的格式,保证服务器能够对其正 常的解析,正常情况下,data的格式为查询字符串形式(query string),如下:
1 |
data = "name=hmj&password=123456" ; |
需要注意的是,如果open的method指定为post,name比必须显示的通过setRequestHeader方法对本次请求的MIME-Type进行如下:
1 |
xhr.setRequestHeader("Content-Type" , "application/x-www-form-urlencoded"); |
该设置指定post中的数据以form表单域的格式进行提交,如果不进行此设置那么post过去的数据服务器无 法解析。
abort()
取消本次请求的执行流程,调用此方法会打断正在执行过程中的异步请求,onreadystatechange中的回调函数将被移除,readyState将重置为0。XMLHttpRequest对象将被重新置为初始化状态。对于同一个XMLHttpRequest对象, 在其调用abort方法之后,下次发送请求需要重新调用open方法,执行初始化流程。
getResponseHeader()
以字符串形式,获取指定HTTP消息头的值。如果响应未返回,或响应失败,或指定的消息头名称在响应中不存 在,该方法调用会返回null值。
调用形式:
getResponseHeader(header);
参数说明:
header:
http消息头名称
如在上述的request.html例子中,我们可以在onreadystatechange回调中获取响应的Content-Type值
1 |
console.log(xhr.getResponseHeader("Content-Type")) |
结果为:
1 |
text/html;charset=UTF-8 |
getAllResponseHeaders()
以字符串的形式返回本次请求对应的所有Http响应消息头信息,如果响应尚未返回该方法调用返回null。
如下:
1 |
Date: Thu, 09 Aug 2012 01:10:02 GMT |
5 |
Server: Apache-Coyote/1.1 |
7 |
Content-Type: text/html;charset=UTF-8 |
注意项:
1.获取异步数据
当xhr的open方法的async属性为true时,发送的请求为异步请求,为xhr的onreadystatechange添加回调函数后,每当readyState属性发生变化时,都会调用该函数,所以我们需要检测xhr的readyState的当前值来获取我们的数据,如:
02 |
xhr.onreadystatechange = function(){ |
04 |
if(xhr.readyState == 4){ |
06 |
if((xhr.status>=200 && xhr.status < 300) || xhr.status == 304){ |
07 |
alert("响应成功,可以获取数据"+xhr.responseText); |
09 |
alert("响应失败"+xhr.status) |
2.setRequestHeader
虽然我们可以利用此方法设置标准的HTTP消息头,常见的消息头有:
Accept:浏览器可以处理的内容类型
Accept-Charset:浏览器可以显示的字符集类型
Accept-Encoding:浏览器的压缩编码方式
Accept-Language:浏览器使用的语言
Host:发送本请求页面的domain
Cookie:当前页面中的cookie
Connection:浏览器与服务器之间的链接类型
Referer:发出请求的页面的URI
User-Agent:浏览器的user-agent字符串
下面是一个完整的ajax请求的http头信息示例:
01 |
POST /lib/tools/ajax/receive.jsp HTTP/1.1 |
05 |
Connection: keep-alive Content-Length: 9 |
07 |
Origin: http://localhost:8008 |
09 |
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11 |
11 |
Content-Type: application/x-www-form-urlencoded |
15 |
Referer: http://localhost:8008/lib/tools/ajax/SimpleCode.html |
17 |
Accept-Encoding: gzip,deflate,sdch |
19 |
Accept-Language: zh-CN,zh;q=0.8 |
21 |
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3 |
23 |
Cookie: JSESSIONID=1932B2931ACC72E4BF92C29E9FCF1969 |
利用该方法我们也可以设置我们自定义的消息和消息值,如xhr.setRequestHeader("myHeader" , "myValue");另外前面也提到过,该方法一定要在open方法和send方法之间调用。
3.timeout
虽然各种浏览器对ajax都有较好的支持,但是仍存在一些兼容性的问题,虽然这不是本文介绍范围之内的东西,但是timeout属性作为一个常用的属性,我们还是有必要提及下。IE8中首先引入了timeout属性和ontimeout事件,方便我们设置服务器响应超时处理。我们可以通过xhr的timeout属性设置服务器响应的最大等待时间(以毫秒为单位),当服务器的响应时间超出该设置后,xhr的ontimeout事件将会触发,如下
01 |
xhr.open("POST" , "./receive.jsp"); |
03 |
xhr.onreadystatechange = function(){ |
05 |
if(xhr.readyState == 4){ |
07 |
if((xhr.status>=200 && xhr.status < 300) || xhr.status == 304){ |
08 |
alert("响应成功,可以获取数据"+xhr.responseText); |
10 |
alert("响应失败"+xhr.status) |
13 |
//timeout exception todo |
18 |
xhr.ontimeout = function(){ |
本例中我们设置超时时间为一千毫秒,当服务器响应超过该时间段后,会调用ontimeout的事件处理函数,此时xhr会被自动取消,但是onreadystatechange 回调仍然会继续并且readyState会设置为4,此时访问xhr的status/statusText/responseText/responseXML都会出现错误,所以我们需要利用try catch处理。由于并不是所有浏览器都支持timeout属性和ontimeout事件,所以我们可以用以下代码解决该问题,
03 |
xhr.open("POST" , "./receive.jsp"); |
04 |
xhr.onreadystatechange = function(){ |
06 |
if(xhr.readyState == 4){ |
08 |
if((xhr.status>=200 && xhr.status < 300) || xhr.status == 304){ |
09 |
alert("响应成功,可以获取数据"+xhr.responseText); |
11 |
alert("响应失败"+xhr.status) |
14 |
//timeout exception todo |
19 |
timerId = setTimeout(function(){ |
20 |
xhr.onreadystatechange = function(){}; |
上述例子中,利用setTimeout函数可以简单的模拟超时设置,通过调用xhr的abrot方法来取消响应,来达到基本一致的效果
总结:
本文只是简单的介绍了Ajax相关的基本概念,历史,基本特点,以及关于Ajax核心对象XMLHttpRequest的相关介绍,包括XMLHttpRequest发展历史,基本的属性和方法。本文内容多数来源于网络,仅供参考,请有选择性的阅读理解,并欢迎指正。另外本文未涉及到的关于Ajax的内容包括,兼容性问题,安全性问题,跨域访问问题,编码问题,缓存问题,期待以后可以有时间再次关于这些问题做些简单的整理。
参考:
MDN XMLHttpRequest
IE MSDN XMLHttpRequest
WIKI XMLHttpRequest
Baidu Tangram-base,