JavaScript - Ajax 总结

markdown

2005年 Jesse James Garrett 发表了一篇文章,介绍了 Ajax 这门技术。这种技术能够向服务器请求额外的数据而无需刷新页面,带来了很好的用户体验,一时间席卷全球。

XMLHttpRequest

Ajax 技术的核心是 XMLHttpRequest 对象(简称XHR)。这是微软首先引入的一个特性,其他浏览器后来也都有相同的实现。XHR 为向服务器请求和解析服务器响应提供了流畅的接口。用异步的方式从服务器获取更多信息。这意味着,只要用户触发了某一事件,再不刷新网页的情况下,更新服务器最新数据。

虽然 Ajax 中的 X 代表 XML ,但 Ajax 通信和数据格式无关,也就是说这种技术不一定使用 XML。

XHR使用方法

XHR 对象的使用方法总体上分为三步:
第一步: 创建 XMLHttpRequest 对象。
W3C 支持原生的 XMLHttpRequest 对象,IE6使用的是 MSXML 库中的 ActiveXObject 对象,所以要做兼容!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function createXHR(){
if(typeof XMLHttpRequest != 'undefined'){
return new XMLHttpRequest();
}else if(typeof ActiveXObject != 'undefined'){
var version = [
'MSXML2.XMLhttp.6.0',
'MSXML2.XMLhttp.3.0',
'MSXML2.XMLhttp'
];
for(var i = 0;i < version.length;i++){
try{
return new ActiveXObject();
}catch(e){
//跳过
}
}
}else{
throw new Error('不支持Ajax!!');
}
}
var xhr = createXHR(); //创建对象

第二步: 准备发送请求用 open() 方法,接受三个参数:发送方式(get/post)、请求的 url、同步或者异步(同步为false、异步为true)

1
xhr.open('get','demo.php','false');

php 文件内容为:

1
2
3
<?php
echo Date('Y-m-d H:i:s'); //当前时间
?>

第三步: 发送请求 send() 方法 ,接受一个参数作为请求主体发送的数据(非 post 方式一般填 null 即可)

1
xhr.send(null);

同步方式

当请求发送到服务器端,收到响应后,相应的数据会自动填充 XHR 对象的属性。一共有四个属性:

属性 返回值
responseText 作为响应主体被返回
responseXML 如果响应主体内容类型是 text/xmlapplication/xml,返回包含响应数据的 XMLDOM 文档
status 响应的 HTTP 状态
statusText HTTP 状态的说明

接受响应之后,第一步检查 status 属性,以确定响应已经成功返回。一般把 HTTP 状
态代码为 200 作为成功的标志。
(304 代表本地缓存中存在请求的页面缓存,也是响应成功的标志)

1
2
3
4
5
6
7
8
9
10
document.onclick = function(){
var xhr = createXHR();
xhr.open('get','demo.php',false);
xhr.send(null);
if(xhr.status == 200){
alert(xhr.responseText);
}else{
alert('请求失败');
}
}

注意: 同步方式 send() 后才能收到返回数据

异步方式:

使用异步调用才是我们真正常用的手段。使用异步调用的时候 ,需要触发 readystatechange 事件,然后检测 readyState 属性值。值为 4 时表示 接受到全部响应数据,并且可以使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
document.onclick = function(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){ //完全响应
if(xhr.status == 200){
alert(xhr.responseText);
}else{
alert('请求失败!');
}
}
}
xhr.open('get','demo.php',true);
xhr.send(null);
}

PS: readystatechange 事件要放在发送请求之前!!!

GET方式

GET方式主要用于向服务器请求获取数据,反应在 URL 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
document.onclick = function(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200 || xhr.status == 304){
alert(xhr.responseText);
}else{
alert('Ajax错误!');
}
}
};
var url = 'demo.php';
url = addURLParam(url,'name','cwyaml');
url = addURLParam(url,'age',100);
xhr.open('get',url,true);
xhr.send(null);
}
//编码函数
function addURLParam(url,name,value){
url += url.indexOf('?') == -1 ? '?' : '&';
url += encodeURIComponent(name) + '=' + encodeURIComponent(value);
return url;
}

POST方式

send() 执行之前需要修改头部信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
document.onclick = function(){
var xhr = createXHR();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200 || xhr.status == 304){
alert(xhr.responseText);
}else{
alert('Ajax错误!');
}
}
};
var url = strParam('name','cwyaml');
url += strParam('age',23);
xhr.open('get','demo.php',true);
xhr.send(url);
}
//字符编码
function strParam(name,value){
return encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
}

浏览器兼容

这里的实现方式和 JQuery 实现 Ajax 的方式相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//创建对象
function createXHR(){
if(typeof XMLHttpRequest != 'undefined'){ //W3C
return new XMLHttpRequest();
}else if(typeof ActiveXObject != 'undefined'){ //IE
var version = [
'MSXML2.XMLHttp.6.0',
'MSXML2.XMLHttp.3.0',
'MSXML2.XMLHttp'
];
for(var i = 0;i < version.length;i++){
try{
return new ActiveXObject(version[i]);
}catch(e){
//跳过!
}
}
}else{
throw new Error('不支持Ajax!');
}
}
//Ajax请求
function ajax(obj){
var xhr = createXHR();
obj.date = strParam(obj.date); //字符编码是为了解决IE缓存问题
//判断异步
if(obj.async === true){
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
callback();
}
}
}
//判断get
if(obj.method === 'get'){
obj.url = obj.url + '?rand=' + Math.random() + '&' + obj.date;
xhr.open(obj.method,obj.url,obj.async);
xhr.send(null);
}
//判断post
if(obj.method === 'post'){
xhr.open(obj.method,obj.url,obj.async);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send(obj.date);
}
//判断同步 同步要放在 send 后面!!!一定要注意
if(obj.async === false){
callback();
}
function callback(){
if(xhr.status == 200){
obj.success(xhr.responseText);
}else{
alert('Ajax错误!');
}
}
}
//字符编码
function strParam(date){
var arr = [];
for(var i in date){
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(date[i]));
}
return arr.join('&');
}
//调用方法
document.onclick = function(){
ajax({
method : 'get',
url : 'demo.php',
date:{
'name' : 'cwyaml',
'age' : 123
},
success : function(text){
alert(text);
},
async : true
});
}

总结

在写浏览器兼容的时候出现了一个问题:
同步请求时 xhr.status 始终返回 0 。这令我百思不得其解,检查代码好几遍没发现错误,上百度找解决办法也没有结果,折腾了一上午时间才发现原来 把同步请求写在了 send() 前面 。我也是醉了,同步是单线程的,不发送请求怎么能得到返回数据。
不过这也是学习中的一点乐趣,解决问题后的心情还是很爽的。以后一定要细心再细心,不要再这些小问题上浪费太多时间。