Ajax是Asynchronous Javascript and XML的缩写。它不是一门语言,只是一种开发技术,能够在不卸载页面的情况下向服务器请求数据并更新局部的Dom,比起以往Web的“单击、等待”方式用户体验得到了极大的提升。
XMLHttpRequest 是 AJAX 的基础。最初由微软引入的一个新特性。
XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
1、创建 XMLHttpRequest 对象
所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。
var xhr = new XMLHttpRequest();
IE5,6需要使用 ActiveX 对象:
var xhr = new ActiveXObject("Microsoft.XMLHTTP");
兼容写法:
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
2、如何向服务器发送请求呢?
XMLHttpRequest提供了两个方法,open
和send
方法 | 描述 |
---|---|
open (method, url, async) | 规定请求的类型、URL 以及是否异步处理请求。 method:请求的类型;GET 或 POST url:文件在服务器上的位置 async:true(异步)或 false(同步) |
send(string) | 将请求发送到服务器。 string:仅用于 POST 请求 |
注:
url是相对于执行代码的当前页面的相对路径,也可以使用绝对路径。
调用open并不会真正发送请求,只是启动一个请求以备发送。
GET方式和POST有什么区别?
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
无法使用缓存文件(更新服务器上的文件或数据库)
向服务器发送大量数据(POST 没有数据量限制)
发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
例:
Get:
xhr.open("get", "example.php", true);
xhr.open("get", "example.php?t="+Math.random(), ture);//在上面的例子中,可能得到的是缓存的结果。为了避免这种情况,请向 URL 添加一个唯一的 ID。
xhr.open("get", "example.php?name=name1&age=age1", ture);
POST:
xhr.open("post", "example.php", true);
也可以像 HTML 表单那样 POST 数据,请使用 setRequestHeader()
来添加 HTTP 头。然后在 send()
方法中规定您希望发送的数据:
xhr.open("post","example.php",true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("fname=Bill&lname=Gates");
send()方法,用于发送请求。此方法接受一个参数,即一个座位请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入null,send(null)
,因为对于有些浏览器来说是必须的。调send方法后,请求就会被分派到服务器。
还可以使用bort()
方法来取消异步请求,调用这个方法后,XHR就会停止触发事件,这时不再允许访问与响应有关的对象属性。
xhr.abort();
注:在终止请求之后,还应该对XHR对象进行解引用操作。由于内存原因,不建议重用XHR对象。
xhr = null;
3.服务器响应数据
服务器的响应信息都存放在xhr的属性中
xhr.responseText;// 字符串形式的响应数据。
xhr.responseXML;// 获得 XML 形式的响应数据。如需其中数据需要对XML进行解析。
xhr.status;// 响应的HTTP状态
xhr.statusText;// HTTP状态的说明
4.readystatechange事件
此事件每当readyState改变时就会触发,因为要响应改变,所以应该在open()方法调用之前指定处理函数onreadystatechange
。
AJAX指的是异步XHR请求,也就是async为ture。当数据请求处理完成时,可以使用onreadystatechange
事件函数处理。
xhr.onreadystatechange () {
if (xhr.readyState == 4 && xhr.status ==200 ){
document.getElementById("box").innerHtml = xhr.responseText;
}
}
xhr.open("get", "example.php", true);
xhr.send();
但是async为false时不需要指定onreadystatechange事件,只需要要继续执行的代码写在数据请求语句的后面便可。
xhr.open("get", "example.php", false);
xhr.send();
document.getElementById("box").innerHtml = xhr.responseText;
5.XMLHttpRequst 属性
属性 | 描述 |
---|---|
onreadystatechange | readystatechange的处理函数,每当 readyState 属性改变时,就会调用该函数。 |
readyState | 存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。 0: 请求未初始化。尚未调用open()方法 1: 启动。服务器连接已建立。已调用open()未调用send() 2:发送。 请求已接收。已调用send(),但未收到响应 3: 接收。请求处理中。已经收到部分响应数据 4:完成。 请求已完成,且响应已就绪。数据接受完成,并可以在客户端使用 |
status | 响应的HTTP状态 200: “OK” 304: 资源未被修改,可以使用缓存数据 404: 未找到页面 |
statusText | HTTP状态说明 |
(xhr.status >= 200 && xhr.status <300) || xhr.status == 304)//表示请求成功
6.HTTP头部信息
每个HTTP请求和响应都会带有相应的头部信息,XHR对象也提供了操作者两种头部信息的方法。
setRequestHeader(); //方法可以设置自定义的请求头部信息,有两个参数,一个是头部字段的名称,另一个是头部字段的值。要成功发送请求头部信息,必须在调用o pen方法之后,调用send方法之前调用此方法。
getResponseHeader(str);//可以获取指定的头部字段名为str的响应头部信息。
getAllResponseHeaders()//可以获取所有的响应头部信息组成的长字符串
7.进度事件
loadstart 在接收到数据的第一个字节时触发
progress 在接收响应期间持续触发
error 在请求发生错误时触发
abort 在因为调用abort()方法而终止时触发
load 在接收到完整的响应数据时触发
loadend 在通信完成或者触发error、aboart或load事件后触发
但他们在兼容性上有差异。
8.跨源资源共享
跨源资源共享也叫CORS,是Cross-Origin Resource Sharing的简称,是为了解决XHR的同源策略的束缚。XHR的同源策略为通信设置了“相同的域,相同的端口,相同的协议”这一限制,试图访问上述限制之外的资源都会引发安全错误。
CORS的基本思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应食应该成功,还是应该失败。
IE8的实现
通过一个类似于XHR的新对象XDR(XDomainRequest),它部分实现了W3C的CORS规范。XDR与XHR的不同之处在于:
- cookie不会随请求发送,也不会随响应返回
- 只能设置请求头部信息中的Content-Type字段
- 不能访问响应头部信息
- 只支持GET和POST请求
使用方法:
var xdr = new XDomainRequest();
xdr.onload = function(){//响应触发事件处理程序
alert(xdr.responseText);//信息还是保存在responseText中
}
xdr.onerror = function(){//失败触发事件处理程序
alert("An error occurred");
}
xdr.timeout = 1000;//设置超时,超过时间就会触发timeout事件
xdr.ontimeout = funtion(){ //超时处理程序
alert("Request took too long");
}
xdr.open("get", "http://www.somewhere.com/page/");//无async,因为XDR只能发送异步请求。
xdr.send(nul);
只要响应就会触发load事件,如果失败则触发error事件。
在请求返回前调用xdr.abort()
可以终止请求。
为了支持POST请求,XDR对象提供了contentType属性,用来表示发送数据的格式。这个属性是XDR对象影响头部信息的唯一方式。
...
xdr.open("post", "http://www.somewhere.com/page/");
xdr.contentType = "application/x-www-form-urlencoded";
xdr.send("name=value1&age=value2");
其他浏览器对CORS的实现
实现了CORS的这些浏览器,通过XHR对象实现了CORS的原生支持。只需要在请求的open()方法中传入绝对的路径
便可。
xhr.open("get", "http://www.somewhere.com/page/", true);
xhr.send(null);
跨域的xhr依然可以访问status和statusText。但它也有所限制:
- 不能使用setRequestHeader()设置自定义头部。
- 不能发送和接受cookie
- 调用getAllRequestHeaders()方法总会返回空字符串
由于同源请求和跨源请求都使用相同的接口,因此建议,本地资源使用相对URL,远程资源使用绝对URL。以示区别。
Preflighted Requests
占位
跨浏览器的CORS
首先检查XHR是否支持CORS,也就是检查withCredentials属性,再检测XDomainRequest是否存在
function createCORSRequest(method,url){
var xhr = new XMLHttpRequest();
if("withCredentials" in xhr) {
xhr.open(method,url,true)
} else if (window.XDomainRequest) {
xhr = new XDomainRequest();
xhr.open(method,url);
} else {
xhr = null;
}
return xhr;
}
var request = createCORSRequest("get","http://www.somewhere.com/page/");
if (request) {
request.onload = function(){
alert(request.responseText);
};
request.send(null);
}
其他跨域技术
1、图像Ping。原理:网页可以从任何网页中加载图像,不用担心是否跨域。
var img = new Image();
img.onload = img.error = function(){
//doSomething
};
img.src = "http://www.somewhere.com/page/test?name=value1";
图像Ping,浏览器得不到任何具体的数据,只能通过时间监听其响应状态。并且,它只能发送GET请求,因此只能用于浏览器与服务器的单向通信。
2、JSONP
JSONP是JSON with padding(填充式JSON或参数式JSON)的简写。
JSONP由两部分组成:对调函数和数据。对调函数是响应到来时应该在页面中调用的函数,回调函数的名字一般是在请求中指定的,而数据就是传入回调函数的JSON数据。
callback({"name": "张三"});//JSONP格式
http://freegeoip.net/json/?callback=handleResponse ; //典型的JSONP请求,handleResponse是回调函数名
JSONP是通过动态<script>
元素来使用的,使用时可以为src属性指定一个跨域URL。JSONP是有效的javascript代码,所以在请求完成后,即在JSONP响应加载到页面中以后,就会立即执行。
function handleResponse () {
//doSomething
}
var script = document.creatElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild);
JSONP可以直接访问响应文本,且支持双向通信。
JSONP的不足在于,安全以及确定是否失败。
3、Comet
ajax是页面向服务器请求数据,Comet是服务器向页面推送数据。
实现Comet的方式有两种:长轮询和流
既然有长轮询,就一定有短轮询,短轮询也称传统轮询。短轮询是浏览器间隔一定时间就向服务器发送请求,服务器立即响应。
长轮询:页面发起一个请求到服务器,服务器一直保持连接打开,直到有数据可以发送。发送完数据之后,浏览器关闭连接,随即又发送一个新请求到服务器。
长轮询和短轮询,浏览器都要在接收数据之前先发起对服务器的连接,两者的最大区别在于,服务器如何响应并发送数据。
流:流在整个生命周期内只使用一个HTTP连接。浏览器只像服务器发送一份请求,服务器保持连接打开,然后周期性的向浏览器发送数据。
在非IE浏览器中通过侦听readystatechange事件及检测readyState的值是否为3就可以利用XHR实现HTTP流。因为每次结果都会缓存在XHR的responseText中,因此需要比较之前接受到的数据,处理后才能拿到最新的数据。
4、SSE
SSE(Server-Sent Events)服务器发送事件,它是围绕只读Comet交互推出的API或者模式。SSE API用于创建页面到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。服务器响应的MIME类型必须是text/evnet-stream,而且是浏览器中的javascript API能解析格式输出。SEE支持长轮询,短轮询和HTTP流,而且在断开连接时自动确定何时重新连接。
var source = new EventSource("example.php");
传入的URL必须同源,EventSource实例有一个readyState属性,0 表示正在连接到服务器,1表示打开了连接,2表示关闭了连接。同时还有三个事件:
- open 在建立连接时触发
- message 从服务器接收到新事件时触发
- error 在无法建立连接时触发
message处理函数有一个参数event对象,服务器返回的数据保存在event.data中。
默认情况下,EventSource对象会保持与服务器的活动连接。如果断开连接,还会重新连接。这表明SSE更适合长轮询和HTTP流。如果想要强制断开连接不再重连,可以使用close()方法
source.close();
5.Web Sockets
目标是在一个单独的持久连接上提供全双工、双向通信。在Javascript建立了Web Socket之后,会有一个HTTP请求发送到浏览器以发起连接。在取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为Web Socket协议。标准的HTTP服务器无法实现Web Sockets,只有支持这种协议的服务器才能正常工作。
URL格式使用ws://
,加密的连接wss://
Web Sockets API
var socket = new WebSocket("ws://www.example.com/server.php");//URL必须是绝对路径,因此可以通过它打开到任何站点的连接
WebSocket也有readyState属性:
- WebSocket.OPENING (0):正在建立连接
- WebSocket.OPEN (1):已经建立链接
- WebSocket.CLOSING (2):正在关闭连接
- WebSocket.CLOSE (3):已经关闭连接
要关闭Web Socket连接,可以在任何时候调用close()方法
socket.close();//调用之后readyState值立即变为2,关闭后则变为3.
建立连接后就可以接收和发送数据了。要向服务器发送数据,使用send()方法,并传入任意字符串
socket.send("Hello world");
Web Sockets只能通过连接发送纯文本,所以复杂数据必须序列化。
当服务器向客户端发来消息时,WebSocket对象就会触发message事件,返回的数据仍在evnet.data
中。WebSocket对象还有其他三个事件,在连接生命周期的不同阶段触发
- open 在成功建立连接时触发
- error 在发生错误时触发,连接不能持续。
- close 在连接关闭时触发
WebSocket对象不支持DOM 2级事件侦听器,因此必须使用DOM 0级语法分别定义每个事件处理程序。
socket.open = function () {}
其中close 事件的event对象含有额外的信息,有三个额外属性:wasClean、code、reason。其中wasClean是一个布尔值,表示连接是否已经明确的关闭;code是服务器返回的数值状态码;而reason包含服务器发回的消息。