AJAX
最后更新于:2022-04-01 23:53:57
## AJAX
超文本传输协议(HyperText Transfer Protocol,HTTP)是用于从WWW服务器传输超文本到本地浏览器的传输协议(transport)。它可以使浏览器更加高效,使网络传输减少。
Ajax(Asynchronous JavaScript and XML)描述了一种主要使用脚本操纵HTTP的Web应用架构。
Ajax的主要特点是使用脚本操纵HTTP和Web服务器进行数据交换,不会导致页面重载。
AJAX技术的核心是XMLHttpRequest对象(简称XHR)。
**一、XMLHttpRequest对象**
所有浏览器(IE7之前除外)都支持XMLHttpRequest对象,它定义了用脚本操纵HTTP的API。除了常用的GET请求,这个API还包含实现POST请求的能力,同时它能用文本或Document对象的形式返回服务器的响应。
浏览器在XMLHttpRequest类上定义了它们的HTTP API,这个类的每个实例都表示一个独立的请求/响应对,并且这个对象的属性和方法允许指定请求细节和提取响应数据。
具体来说,AJAX包括以下几个步骤:
- 创建AJAX对象(实例化XMLHttpRequest对象)
- 发起HTTP请求
- 接收服务器传回的数据
- 更新网页数据
**1.1实例化**
实例化XMLHttpRequest对象:
```
var xhr = new XMLHttpRequest();
```
注意:你可以重用已存在的XMLHttpRequest,但这将会终止之前通过该对象挂起的任何请求。
由于IE7之前的版本不支持非标准的XMLHttpRequest()构造函数,不过能如下模拟:
```
function createXHR(){
if ( typeof XMLHttpRequest != 'undefined'){
return new XMLHttpRequest();
}else if(typeof ActiveXObject != 'undefined'){
if(typeof arguments.callee.activeXString != 'string'){
var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'], i, len;
for(i = 0, len = versions.length; i < len; i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
}catch(e){
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}else{
throw new Error('No XHR object available');
}
}
```
一个HTTP请求由4部分组成:
- HTTP请求方法或动作(verb)
- 正在请求的URL
- 一个可选的请求头集合,其中可能包括身份验证信息
- 一个可选的请求主体
服务器返回的HTTP响应包含3部分:
- 一个数字和文字组成的状态码,用来显示请求的成功和失败
- 一个响应头集合
- 响应主体
注意:使用XMLHttpRequest时,必须把文件放到Web服务器上。因为AJAX只能向同源网址(协议、域名、端口都相同)发出HTTP请求,如果发出跨源请求,就会报错
**1.2 指定请求**
**1.2.1 open()**
创建XMLHttpRequest对象后,就可以调用XMLHttpRequest对象的open()方法去指定这个请求的两个必需部分:方法和URL。
```
xhr.open('GET','example.php');
```
上面代码向指定的服务器网址,启动GET请求。
open()的第一个参数指定HTTP方法或动作(常用的是“GET”和“POST”),这个字符串不区分大小写,但通常使用大写字母来匹配HTTP协议。“DELETE”、“HEAD”、“OPTONS”、“PUT”也可以作为open()方法的第1个参数。
第二个参数是URL(跨域的请求通常会报错),它是请求的主题。
还有第三个可选参数,表示是否异步发送请求的布尔值(true同步,false异步,默认false)
调用open()方法并不会真正发送请求,而只是启动一个请求以备发送。
**1.2.2 HTTP头部信息**
每个HTTP请求和响应都会带有相应的头部信息。
XHR对象也提供了操作这两种头部(即请求头部和响应头部)信息的方法。
默认情况下,在发送XHR请求的同时,还会发送下列头部信息:
```
Accept: 浏览器能够处理的内容类型
Accept-Charset: 浏览器能够显示的字符集
Accept-Encoding:浏览器能够处理的压缩编码。
Accept-Language: 浏览器当前设置的语言。
Connection:浏览器与服务器之间连接的类型
Cookie:当前页面设置的任何Cookie
Host:发出请求的页面所在的域。
Referer:发出请求的页面的URI。
User-Agent:浏览器的用户代理字符串
```
如果有请求头,我们可以设置它。例如,POST请求需要“Content-Type”头指定请求主题的MIME类型。
```
xhr.setRequestHeader('Content-Type','text/plain');
```
注意:如果对相同的头调用setRequestHeader()多次,新值不会取代之前指定的值,相反,HTTP请求将包含这个头的多个副本或这个头将指定多个值。
必须在调用open()方法只会且调用send()方法之前调用setRequestHeader()。
**1.2.3 send()**
使用XMLHttpRequest发起HTTP请求的最后一步是指定可选的请求主体的数据并向服务器发送它。
```
xhr.send(null);
```
GET请求没有主体,所以应该传递null或者省略这个参数。POST请求通常拥有主体,同时它应该匹配使用setRequestHeader()指定的“Content-Type”头。
HTTP请求的各部分有指定顺序:请求方法和URL首先到达,然后是请求头,最后是请求主体。XMLHttpRequest实现通常直到调用send()方法才开始启动网络。
**1.2.4 GET请求**
GET是最常见的请求类型,必要时,可以将查询字符串参数追加到URL的末尾,以便将信息发送给服务器。
注意:对于XHR来说,传入open()方法的URL末尾的查询字符串必须经过正确的编码,否则可能出现字符串格式的错误。
查询字符串中每个参数的名称和值都必须使用encodeURIComponent()进行编码,然后才能放到URL的末尾,而且所有名值对都必须由和号(&)分隔。如下所示:
```
xhr.open('get','example.php?name1=value1&name2=value2",true);
```
下面这个函数可以辅助向现有的URL的末尾添加查询字符串参数:
```
function addURLParam(url, name, value){
url += (url.indexOf('?') == -1) ? '?' : '&';
url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
return url;
}
```
**1.2.5 POST请求**
对于POST请求,应该把数据作为请求的主题提交,它不是以地址形式传参,而是在send()中传参。
注意:使用POST请求时,如果不设置Content-Type头部信息,那么发送给服务器的数据就不会出现在`$_POST`中。
**1.3 取得响应**
一个完整的HTTP响应由`状态吗`、`响应头集合`和`响应主体`组成。这些都可以通过XMLHttpRequest对象的属性和方法使用:
- `status`和`statusText`属性以数字和文本的形式返回HTTP状态码。这些属性保存标准的HTTP值。像200和“OK”表示成功请求,404和“Not Found”表示URL不能匹配服务器上的任何资源。
- 使用`getResponseHeader()`和`getAllResponseHeaders()`能查询/获取响应头。XMLHttpRequest会自动处理cookie:它会从getAllResponseHeaders()头返回集合中过滤cookie头,而如果给getResponseHeader()传递“Set-Cookie”和“Set-Cookie2”,则返回null。
- 响应主体可以从responseText属性得到文本形式的,从responseXML属性中得到Document形式的。
为了在响应准备就绪时得到通知,我们必须监听XMLHttpRequest对象上的`readystatechange`事件。
每当发生状态变化的时候,readyState属性的值就会发生改变。这个值每一次变化,都会触发readyStateChange事件。
```
xhr.onreadystatechange=function(){}
```
**1.3.1 同步响应**
XMLHttpRequest默认是异步的,当然,如果有需要,我们也可以设置成同步(较少用):
```
xhr.open('GET','example.php',false);
```
上面的代码中,通过传递第三个参数为false实现同步。要注意的是:一旦设置为同步,那么send()方法将阻塞直到请求完成。
**1.3.2 响应解码**
如果服务器发送诸如对象或数组这样的结构化数据作为响应,它应该传输JSON编码的字符串数据。
下面是一个解析不同类型的响应值的方法:
```
function get(url,callback){
var xhr = new XMLHttpRequest(); //创建新请求
xhr.open('GET',url);
xhr.onreadystatechange=function(){
//如果请求完成且成功
if(xhr.readyState === 4 && xhr.status === 200){
//获得响应的类型
var type = xhr.getResponseHeader('Content-type');
if(type.indexOf('xml') !== -1 && xhr.responseXML){
callback(xhr.responseXML); //Document对象响应
}else if(type === 'application/json'){
callback(JSON.parse(xhr.responseText)); //JSON响应
}else{
callback(xhr.responseText); //字符串响应
}
}
};
xhr.send(null); //立即发送请求
}
```
**1.4 编码请求主体**
HTTP POST请求包括一个请求主体,它包含客户端传递给服务器的数据。
**1.4.1 表单编码的请求**
默认情况下,HTML表单通过POST方法发送给服务器,而编码后的表单数据则用做请求主体。例如:
```
user=TG&age=18;
```
表单数据编码格式有一个正式的MIME类型:
```
application/x-www-form-urlencoded
```
当使用POST方法提交这种顺序的表单数据时,必须设置“Content-Type”请求头为这个值。
```
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
```
注意:当使用form时,这不是必需值,因为这是默认方法。
当我们发送给服务器的是一个JavaScript对象时,我们也可以采取这种类型的编码。
下面的函数encodeFormData()就是将对象转换为表单编码。
```
/*
* {
* name:'TG',
* age:18
* }
*/
function encodeFormData(data){
if(!data) return '';
var pairs = [];
for(var name in data){
if(!data.hasOwnProperty(name)) continue;
if(typeof data[name] === 'function') continue;
var value = data[name].toString();
name = encodeURIComponent(name.replace('%20','+'));
value = encodeURIComponent(value.replace('%20','+'));
pairs.push(name + '=' + value);
}
return pairs.join('&');
}
```
**1.4.2 JSON编码的请求**
使用JSON编码主体来发起HTTP POST请求
```
xhr.setRequestHeader('Content-Type','application/json');
xhr.send(JSON.stringify(data));
```
**1.5.XMLHttpRequest实例的属性**
**(1)readyState属性**
`readyState`是一个整数,它指定了HTTP请求的状态。它可能的值如下:
- 0,对应常量UNSENT,表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用。
- 1,对应常量OPENED,表示open()已调用,但send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息。
- 2,对应常量HEADERS_RECEIVED,表示send()方法已经执行,并且头信息和状态码已经收到。
- 3,对应常量LOADING,表示正在接收服务器传来的主体(body)部分的数据,如果responseType属性是text或者空字符串,responseText就会包含已经收到的部分信息。
- 4,对应常量DONE,表示服务器数据已经完全接收,或者本次接收已经失败了。
在IE8之前并没有定义这些值,但使用硬编码值4来表示XMLHttpRequest.DONE。
**(2)status**
status属性为只读属性,表示本次请求所得到的HTTP状态码,它是一个整数。一般来说,如果通信成功的话,这个状态码是200。
```
200, OK,访问正常
301, Moved Permanently,永久移动
302, Move temporarily,暂时移动
304, Not Modified,未修改
307, Temporary Redirect,暂时重定向
401, Unauthorized,未授权
403, Forbidden,禁止访问
404, Not Found,未发现指定网址
500, Internal Server Error,服务器发生错误
```
例子:
```
if(xhr.readyState === 4){
//请求完成
if(xhr.status === 200){
//请求成功
}
}
```
**(3)statusText**
statusText属性为只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status属性,该属性包含整个状态信息,比如”200 OK“。
**(4)timeout**
timeout属性等于一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。
对应超时,还有个监听函数:
```
xhr.ontimeout = function(){
//请求超时
}
```
**(5)response**
response属性为只读,返回接收到的数据体(即body部分)。它的类型可以是ArrayBuffer、Blob、Document、JSON对象、或者一个字符串,这由XMLHttpRequest.responseType属性的值决定。
如果本次请求没有成功或者数据不完整,该属性就会等于null。
**(6)responseType**
responseType属性用来指定服务器返回数据(xhr.response)的类型。
```
”“:字符串(默认值)
“arraybuffer”:ArrayBuffer对象
“blob”:Blob对象
“document”:Document对象
“json”:JSON对象
“text”:字符串
```
text类型适合大多数情况,而且直接处理文本也比较方便,document类型适合返回XML文档的情况,blob类型适合读取二进制数据,比如图片文件。
**(7)responseText**
responseText属性返回从服务器接收到的字符串,该属性为只读。如果本次请求没有成功或者数据不完整,该属性就会等于null。
如果服务器返回的数据格式是JSON,就可以使用responseText属性。
```
var data = xhr.responseText;
data = JSON.parse(data);
```
**(8)responseXML**
responseXML属性返回从服务器接收到的Document对象,该属性为只读。如果本次请求没有成功,或者数据不完整,或者不能被解析为XML或HTML,该属性等于null。
返回的数据会被直接解析为DOM对象。
**(9)事件监听接口**
XMLHttpRequest第一版,只能对`onreadystatechange`这一个事件指定回调函数。该事件对所有情况作出响应。 XMLHttpRequest第二版允许对更多的事件指定回调函数。
```
onloadstart 请求发出
onprogress 正在发送和加载数据
onabort 请求被中止,比如用户调用了abort()方法
onerror 请求失败
onload 请求成功完成
ontimeout 用户指定的时限到期,请求还未完成
onloadend 请求完成,不管成果或失败
```
**1.6.XMLHttpRequest实例的方法**
**(1)abort()**
abort方法用来终止已经发出的HTTP请求。
**(2)getAllResponseHeaders()**
getAllResponseHeaders方法返回服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF分隔,如果没有受到服务器回应,该属性返回null。
**(3)getResponseHeader()**
getResponseHeader方法返回HTTP头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,则该属性为null。
如果有多个字段同名,则它们的值会被连接为一个字符串,每个字段之间使用“逗号+空格”分隔。
**(4)open()**
XMLHttpRequest对象的open方法用于指定发送HTTP请求的参数,它的使用格式如下,一共可以接受五个参数。
```
void open( string method, string url, optional boolean async, optional string user, optional string password);
```
参数说明:
- method:表示HTTP动词,比如“GET”、“POST”、“PUT”和“DELETE”。
- url: 表示请求发送的网址。
- async: 格式为布尔值,默认为true,表示请求是否为异步。如果设为false,则send()方法只有等到收到服务器返回的结果,才会有返回值。
- user:表示用于认证的用户名,默认为空字符串。
- password:表示用于认证的密码,默认为空字符串。
**(5)send()**
send方法用于实际发出HTTP请求。如果不带参数,就表示HTTP请求只包含头信息,也就是只有一个URL,典型例子就是GET请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。
**(6)setRequestHeader()**
setRequestHeader方法用于设置HTTP头信息。该方法必须在open()之后、send()之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。
```
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));
```
在上面代码中,首先设置头信息Content-Type,表示发送JSON格式的数据;然后设置Content-Length,表示数据长度;最后发送JSON数据。
**(6)overrideMimeType()**
该方法用来指定服务器返回数据的MIME类型。该方法必须在send()之前调用。
```
// 强制将MIME改为文本类型
xhr.overrideMimeType('text/plain; charset=x-user-defined');
```
在XMLHttpRequest版本升级后,一般采用指定的responseType的方法。
```
xhr.responseType = 'text';
```
**2、 上传文件**
讲到上传文件,我们最常用的就是使用``元素选择文件,然后表单将在它产生POST请求主体中发送文件内容。
```
```
如果要允许选择多个文件,可设置file控件的multiple属性。
```
```
**1.5.1 files属性**
每个``元素都有一个files属性,返回一个FileList对象,它是File对象中的类数组对象,包含了用户选择的文件。
```
var fileInput = document.getElementById('files');
var files = fileInput.files;
```
通常情况下,对于文件上传元素,我们都会添加change事件处理程序,每次文件信息有变化,它都会触发。
```
fileInput.addEventListener('change',function(){
var file = this.files[0];
if(!file) return;
var xhr = new XMLHttpRequest();
xhr.open('POST','upload.php');
xhr.send(file);
},false);
```
文件类型是二进制大对象(Blob)类型中的一个子类型。XHR2允许向send()方法传入任何Blob对象。如果没有显式设置Content-Type头,这个Blob对象的type属性用于设置待上传的Content-Type头。
也可以显式设置:
```
xhr.setRequestHeader('Content-Type', file.type);
```
**1.5.2 multipart/form-data请求**
当HTML表单同时包含文件上传元素和其他元素时,浏览器不能使用普通的表单编码而必须使用成为“multipart/form-data”的特殊Content-Type头来用POST方法提交表单。
**1.5.3 FormData对象**
XHR2定义了新的FormData API,它容易实现多部分请求主体。
首先,新建一个FormData对象的实例,用来模拟发送到服务器的表单数据,把选中的文件添加到这个对象上面。
```
var formdata = new FormData();
```
然后按需多次调用这个对象的append()方法把个体“部分”添加到请求中。
```
for(var i = 0;i < files.length; i++){
var file = files[i];
formdata.append('photos[]',file,file.name);
}
```
最后,把FormData对象传递给send()方法
```
xhr.send(formdata);
```
除了可以添加文件,还可以添加二进制对象(Blob)或者字符串。
```
// Files
formdata.append(name, file, filename);
// Blobs
formdata.append(name, blob, filename);
// Strings
formdata.append(name, value);
```
append方法的第一个参数是表单的控件名,第二个参数是实际的值,第三个参数是可选的,通常是文件名。
**3、HTTP进度事件**
除了使用readystatechange事件来探测HTTP请求的完成外,在XHR2中,还定义了多个有用的事件。
- abort事件:当进度事件被中止时触发。如果发生错误,导致进程中止,不会触发该事件。
- error事件:由于错误导致资源无法加载时触发。
- load事件:进度成功结束时触发。
- loadstart事件:进度开始时触发。
- loadend事件:进度停止时触发,发生顺序排在error事件\abort事件\load事件后面。
- progress事件:当操作处于进度之中,由传输的数据块不断触发。
- timeout事件:进度超过限时触发。
当调用send()时,触发单个loadstart事件。当正在加载服务器的响应时,XMLHttpRequest对象会发现progress事件,通常每隔50毫秒左右,可以使用这些事件给用户反馈请求的进度。当事件完成,会触发load事件。
HTTP请求无法完成有3种情况:
- 请求超时,会触发timeout事件
- 请求终止,会触发abort事件
- 请求发生错误,会触发error事件
注意:对于任何具体请求,浏览器将只会触发load、abort、timeout和error事件中的一个。一旦这些事件中的一个发生后,浏览器应该触发 loadend 事件。
要使用这些事件,有两种方式:
```
xhr.onload=function(){}
xhr.addEventListener('load',function(){})
```
**3.1 progress事件**
因为这些事件是XHR2中才定义的,所以有时需要检查浏览器是否支持progress事件:
```
if('onprogress' in (new XMLHttpRequest())){
//支持progress事件
}
```
除了像type和timestamp这样的常用Event对象属性外,与progress事件相关联的事件对象有3个有用的属性:
- lengthComputable:返回一个布尔值,表示当前进度是否具有可计算的长度。如果为false,就表示当前进度无法测量。
- total:返回一个数值,表示当前进度的总长度。如果是通过HTTP下载某个资源,表示内容本身的长度,不含HTTP头部的长度。如果lengthComputable属性为false,则total属性就无法取得正确的值。
- loaded:返回一个数值,表示当前进度已经完成的数量。该属性除以total属性,就可以得到目前进度的百分比。
我们可以利用total和loaded属性来获取当前进度:
```
xhr.onprogress = function(e){
if(e.lengthComputable){
var percentComplete = e.loaded / e.total;
}
}
```
**3.2 上传进度事件**
在XHR2中,也提供了用于监控HTTP请求上传的事件。在实现这些特性的浏览器中,XMLHttpRequest对象有一个upload属性,upload属性值是一个对象,它定义了addEventListener()方法和整个progress事件集合,比如onprogress和onload。
```
xhr.upload.onprogress = function(e){
var percentComplete = e.loaded / e.total;
}
```
**3.3 中止请求和超时**
我们可以通过调用XMLHttpRequest对象的abort()方法来取消正在进行的HTTP请求,调用abort()方法时,会触发abort事件。
在XHR2中,还定义了timeout属性来指定自动中止后的毫秒数。
```
xhr.timeout = 1000;
```
**4、同源策略**
同源策略是对JavaScript代码能够操作哪些Web内容的一条完整的安全限制。当Web页面使用多个`
';