第二十一章 Ajax与Comet
Ajax是无需刷新页面就能够从服务器取得数据的一种方法,Ajax技术的核心是XMLHttpRequest对象(简称 XHR)。
一、XMLHttpRequest对象
1、由于IE与其他浏览器实现的方式不一样,所以只能用下面方法实现跨浏览器创建XMLHttpRequest对象;
1 function createXHR(){
2 if (typeof XMLHttpRequest != "undefined"){
3 return new XMLHttpRequest();
4 }else if(typeof ActiveXObject != "undefined"){
5 if(typeof arguments.callee.activeXString != "string"){
6 var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],
7 i, len;
8 for (i=0,len=versions.length; i < len; i++){
9 try {
10 new ActiveXObject(versions[i]);
11 arguments.callee.activeXString = versions[i];
12 break;
13 }catch(ex){ } //跳过
14 }
15 }
16 return new ActiveXObject(arguments.callee.activeXString);
17 } else {
18 throw new Error("No XHR object available.");
19 }
20 }
21
22 //创建XHR对象
23 var xhr = createXHR();
2、XHR的用法
主要有三个方法:
open() 方法接收 3 个参数:要发送的请求的类型( "get" 、 "post" 等)、请求的 URL 和表示是否异步发送请求的布尔值。调用 open() 方法并不会真正发送请求,而只是启动 一个请求以备发送。
send()方法接收一个参数:要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入 null。
abort() 方法用于取消异步请求。
3、每个HTTP请求和响应都会带有相应的头部信息,XHR 对象也提供了操作头部(即请求头部和响应头部)信息的方法; GET请求:最常用于向服务器查询某些信息。 POST请求:通常用于向服务器发送应该被保存的数据。
二、XMLHttpRequest2级
1、FormData:FormData为序列化表单以及创建与表单格式相同的数据(用于通过XHR传输)提供了便利:
使用方法:
1 var data = new FormData();
2 data.append("name", "Nicholas");
3
4 //或者传入表单元素
5 var data = new FormData(document.forms[0]);
使用FormData的方便之处体现在不必明确地在XHR对象上设置请求头部。XHR对象能够识别传入的数据类型是FormData的实例,并配置适当的头部信息。
2、超时设定
IE8为XHR对象添加了一个timeout 属性,表示请求在等待响应多少毫秒之后就终止。在给timeout设置一个数值后,如果在规定的时间内浏览器还没有接收到响应,那么就会触发timeout事 件,进而会调用ontimeout 事件处理程序。
3、overrideMimeType()方法:用于重写XHR响应的MIME类型
1 var xhr = new XMLHttpRequest();
2 xhr.open("get", "text.php", true);
3 xhr.overrideMimeType("text/xml" );
4 //xhr.overrideMimeType(text/plain);
5 xhr.send(null);
三、进度事件:
Progress Events规范是W3C的一个工作草案,定义了与客户端服务器通信有关的事件。这些事件 早其实只针对 XHR操作,但目前也被其他 API借鉴。有以下 6个进度事件:
loadstart、progress、error、abort、load、loadend、loadstart;
1、load事件
只要浏览器接收到服务器的响应,不管其状态如何,都会触发load事件。而这意味着你必须要检查 status 属性,才能确定数据是否真的已经可用了,如下:
1 var xhr = createXHR();
2 xhr.onload = function(){
3 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
4 alert(xhr.responseText);
5 } else {
6 alert("Request was unsuccessful: " + xhr.status);
7 }
8 };
9 xhr.open("get", "altevents.php", true);
10 xhr.send(null);
2、progress事件:这个事件会在浏览器接收新数据期间周期性地触发,onprogress事件处理程序会接收到一个event对象,其target属性是XHR对象,但包含着三个额外的属性:lengthComputable、position 和 totalSize。其中,lengthComputable 是一个表示进度信息是否可用的布尔值,position 表示已经接收的字节数,totalSize 表示根据 Content-Length 响应头部确定的预期字节数。
下面展示了为用户创建进度指示器的一个示例:
1 var xhr = createXHR();
2 xhr.onload = function(event){
3 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
4 alert(xhr.responseText);
5 } else {
6 alert("Request was unsuccessful: " + xhr.status);
7 }
8 };
9 xhr.onprogress = function(event){
10 var divStatus = document.getElementById("status");
11 if (event.lengthComputable){
12 divStatus.innerHTML = "Received " + event.position + " of " +
13 event.totalSize +" bytes";
14 }
15 };
16 xhr.open("get", "altevents.php", true);
17 xhr.send(null);
四、跨资源共享
通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源。CORS(跨源资源共享)是 W3C的一个工作草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想,就是使用自定义的 HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。
1、IE对CORS的实现
微软在 IE8中引入了 XDR(XDomainRequest)类型。这个对象与 XHR类似,但能实现安全可靠 的跨域通信。XDR对象的安全机制部分实现了 W3C的 CORS规范。
2、其他浏览器对CORS的实现
Firefox 3.5+、Safari 4+、Chrome、iOS版Safari和Android平台中的WebKit都通过 XMLHttpRequest 对象实现了对CORS的原生支持。
3、跨浏览器的CORS
即使浏览器对CORS的支持程度并不多一样,但所有浏览器都支持简单的(非Preflight和不带凭据的)请求,因此检测XHR是否支持CORS的最简单的方式,就是检查是否存在withCredentials属性。再结合XDomainRequest对象是否存在,就可以兼顾所有浏览器了。
代码:
1 function createCORSRequest(method, url){
2 var xhr = new XMLHttpRequest();
3 if ("withCredentials" in xhr){
4 xhr.open(method, url, true);
5 } else if (typeof XDomainRequest != "undefined"){
6 xhr = new XDomainRequest();
7 xhr.open(method, url);
8 } else {
9 xhr = null;
10 }
11 return xhr;
12 }
13
14 var request = createCORSRequest("get", "http://www.somewhere-else.com/xdr.php");
15 if (request){
16 request.onload = function(){
17 //对request.responseText进行处理
18 };
19 request.send();
20 }
五、其他跨域技术
1、图像Ping:请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或 204 响应。通过图像 Ping,浏览器得不到任何具体的数据,但通过侦听 load 和 error 事件,它能知道响应是什么时候接收到的。图像 Ping 最常用于跟踪用户点击页面或动态广告曝光次数。图像 Ping 有两个主要的缺点,一是只能发送GET 请求,二是无法访问服务器的响应文本。
2、JSONP:JSONP 是被包含在函数调用中的 JSON。JSONP 由两部分组成——回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的JSON数据。与图像 Ping 相比,它的优点在于能够直接访问响应文本,支持在浏览器与服务器之间双向通信。不过,JSONP 也有两点不足,首先,JSONP 是从其他域中加载代码执行,其次,要确定 JSONP 请求是否失败并不容易。
3、Comet
Comet:服务器推送。刚好与Ajax相反,Ajax是一种从页面向服务器请求数据的技术,而Comet则是一种服务器向页面推送数据的技术。Comet能够让信息近乎实时地被推送到页面上,非常适合处理体育比赛的分数和股票涨价。 有两种实现Comet的方式:长轮询和流。
1、长轮询是传统轮询(也称为短轮询)的一个翻版,即浏览器定时向服务器发送请求,看有没有更新的数据。
2、HTTP流就是浏览器向服务器发送一个请求,而服务器保持连接打开,然后周期性地向浏览器发送数据。
使用XHR对象实现HTTP流的典型代码如下所示:
1 function createStreamingClient(url, progress, finished){
2
3 var xhr = new XMLHttpRequest(),
4 received = 0;
5
6 xhr.open("get", url, true);
7 xhr.onreadystatechange = function(){
8 var result;
9
10 if (xhr.readyState == 3){
11
12 //只取得最新数据并调整计数器
13 result = xhr.responseText.substring(received);
14 received += result.length;
15
16 //调用progress回调函数
17 progress(result);
18
19 } else if (xhr.readyState == 4){
20 finished(xhr.responseText);
21 }
22 };
23 xhr.send(null);
24 return xhr;
25 }
26
27 var client = createStreamingClient("streaming.php", function(data){
28 alert("Received: " + data);
29 }, function(data){
30 alert("Done!");
31 });
4、服务器发送事件(SSE):是围绕只读 Comet 交互推出的 API 或者模式。SSE 支持短轮询、长轮询和 HTTP 流,而且能在断开连接时自动确定何时重新连接。
SSE API:用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。
事件流:服务器事件会通过一个持久的 HTTP 响应发送,这个响应的 MIME 类型为 text/event-stream 。响应的格式是纯文本,最简单的情况是每个数据项都带有前缀 data:。
5、Web Sockets:Web Sockets的目标是在一个单独的持久连接上提供全双工、双向通信。在 JavaScript 中创建了 Web Socket 之后,会有一个 HTTP 请求发送到浏览器以发起连接。在取得服务器响应后,建立的连接会使用 HTTP 升级从HTTP 协议交换为 Web Socket 协议。这种协议专门为快速传输小数据设计。虽然要求使用不同的Web 服务器,但却具有速度上的优势。