经过前面两篇文章的详细讲解,我们已经对ajax有了进一步的认识,基于此,
我们完全可以自行封装一个ajax函数,用法和jQuery中的一样,下面我们就开始吧
封装ajax
1,我们本着从简单到复杂的原则,不搞一步到位,一步一步来完成,比如,我们先是完成一个get请求的异步传输
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function ajax(method, url, data, async) {
var xhr = createXHR();
url = url + '?rand=' + Math.random() + '&' + data;
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText);
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
xhr.open(method, url, async);
xhr.send(null);
}
//调用
document.addEventListener('click', function() {
ajax('GET', 'test.php', 'user=guoyu&age=28', true);
}, false);
下面我们来逐步修缮,完善这个ajax函数封装
首先是 ajax函数中当xhr.status == 200时,弹出alert(xhr.responseText);我们肯定不能这么做,当所有都正确的运行到完全获取到服务器返回数据时,我们要拿到返回的数据进行业务上的使用。有人说,可以return xhr.responseText,但是你再仔细看,这是函数里面嵌套的函数,注意作用域的问题,ajax里套了onreadystatechange的事件处理函数。
function a() {
function b() {
return 123;
}
}
alert(a());//undefined
//所以,上述返回的方式,得到的也是undefined
既然不能这样返回,好像只能在封装ajax()的时候操作返回的数据,我们没有办法在调用的时候调用的地方的作用域使用返回数据进行业务开发啊,怎么办?怎么解决这个问题,在调用的时候能 ‘跨越作用域得到服务器返回的数据’ 呢?利用回调函数!
具体做法就是ajax()多带一个参数,这个参数是个对象。对象中比如给个success()函数,如果数据返回成功,会回调success()函数,把返回的数据当做参数传递给success(),这样就可以在调用的时候操作服务器返回的数据了。
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function ajax(method, url, data, async, obj) {
var xhr = createXHR();
url = url + '?rand=' + Math.random() + '&' + data;
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
//alert(xhr.responseText);
obj.success(xhr.responseText); //回调传递参数,在调用端进行操作返回的数据
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
xhr.open(method, url, async);
xhr.send(null);
}
//调用
document.addEventListener('click', function() {
ajax('GET', 'test.php', 'user=XiJP&age=64', true, {
success: function(data) {
alert(data);
}
});
}, false);
以上代码给我们的启示:既然你可以通过对象传参,然后再回调回来,那么其他的参数是不是也可以这样呢,你看ajax()这么多参数,看起来多不好看啊。那我们把所有参数都集中在这个对象里,如下:都是可以的。
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function ajax(obj) {
var xhr = createXHR();
//url = url + '?rand=' + Math.random() + '&' + data;
obj.url = obj.url + '?rand=' + Math.random() + '&' + obj.data;
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
//alert(xhr.responseText);
obj.success(xhr.responseText); //回调传递参数,在调用端进行操作返回的数据
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
xhr.open(obj.method, obj.url, obj.async);
xhr.send(null);
}
//调用ajax()
document.addEventListener('click', function() {
ajax({
method: 'GET',
url: 'test.php',
data: 'user=guoyu&age=27',
success: function(data) {
alert(data);
}
});
}, false);
再说特殊字符问题,上面调用ajax的时候,data如果是data:’user=g&uoyu&age=27’;怎么办,又遇到这个问题了。解决的方法是将data属性从字符串变为对象,对象里面的对象了。当然了,再写个函数,用于解析data这个对象,转换为字符串,同时解决特殊字符问题。
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function params(data) {
var arr = [];
for (var i in data) {
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
}
return arr.join('&');
}
function ajax(obj) {
var xhr = createXHR();
obj.url = obj.url + '?rand=' + Math.random() + '&' + params(obj.data);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
obj.success(xhr.responseText); //回调传递参数,在调用端进行操作返回的数据
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
xhr.open(obj.method, obj.url, obj.async);
xhr.send(null);
}
//调用ajax()
document.addEventListener('click', function() {
ajax({
method: 'GET',
url: 'test.php',
data: {
'name': 'gu&oyu',
'age': 27
},
success: function(data) {
alert(data);
}
});
}, false);
需要提交给服务器的参数解析后追加在URL后面,有可能是”?”也可能是”&”。需要判断一下
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function params(data) {
var arr = [];
for (var i in data) {
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
}
return arr.join('&');
}
function ajax(obj) {
var xhr = createXHR();
//obj.url = obj.url + '?rand=' + Math.random();
obj.data = params(obj.data);
//get时,在URL后加参数,首先判断有没有“?”,也就是判断加进去的参数是不是第一个参数,第一个用?后面用&
if (obj.method == 'GET') {
//obj.url = obj.url.indexOf('?') == -1 ? obj.url+'?'+obj.data : obj+'&'+obj.data;
obj.url += obj.url.indexOf('?') == -1 ? '?'+obj.data : '&'+obj.data;
console.log(obj.url);
}
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
obj.success(xhr.responseText); //回调传递参数,在调用端进行操作返回的数据
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
xhr.open(obj.method, obj.url, obj.async);
xhr.send(null);
}
//调用ajax()
document.addEventListener('click', function() {
ajax({
method: 'GET',
url: 'test.php',
data: {
'name': 'guoyu',
'age': 27
},
success: function(data) {
alert(data);
}
});
}, false);
POST
上面先用GET方式简单写了一次封装,现在轮到POST了,如果是POST,提交的数据就不是跟在URL后面了,而是放在send()里,同时记得设置请求头信息:
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function params(data) {
var arr = [];
for (var i in data) {
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
}
return arr.join('&');
}
function ajax(obj) {
var xhr = createXHR();
obj.url = obj.url + '?rand=' + Math.random();
obj.data = params(obj.data);
if (obj.method == 'GET') {
obj.url += obj.url.indexOf('?') == -1 ? '?'+obj.data : '&'+obj.data;
console.log(obj.url);
}
xhr.open(obj.method, obj.url, obj.async);
if (obj.method == 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(obj.data);
} else {
xhr.send(null);
}
//异步处理的事件函数
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
obj.success(xhr.responseText);
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
}
//调用ajax()
document.addEventListener('click', function() {
ajax({
method: 'POST',
url: 'test.php',
data: {
'name': 'guoyu',
'age': 27
},
success: function(data) {
alert(data);
},
async: true//异步
});
}, false);
图中,两个Array,第一个是PHP中打印的GET参数列表,第二个是PHP中打印的POST参数列表
同步、异步切换
上面的代码只是异步,如果换成同步,就不会弹出PHP服务端返回的数据了,因为还没有针对同步做逻辑处理。
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function params(data) {
var arr = [];
for (var i in data) {
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
}
return arr.join('&');
}
function ajax(obj) {
var xhr = createXHR();
obj.url = obj.url + '?rand=' + Math.random();
obj.data = params(obj.data);
if (obj.method == 'GET') {
obj.url += obj.url.indexOf('?') == -1 ? '?'+obj.data : '&'+obj.data;
console.log(obj.url);
}
xhr.open(obj.method, obj.url, obj.async);
if (obj.method == 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(obj.data);
} else {
xhr.send(null);
}
//异步处理
if (obj.async == true) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {//异步才有的readyState
if (xhr.status == 200) {//200状态码是不分同步异步的
obj.success(xhr.responseText);
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
}
//同步处理放在 open,send之后
if (obj.async == false) {
if (xhr.status == 200) {
obj.success(xhr.responseText);
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}
//调用ajax()
document.addEventListener('click', function() {
ajax({
method: 'POST',
url: 'test.php',
data: {
'name': 'guoyu',
'age': 27
},
success: function(data) {
alert(data);
},
async: false
});
}, false);
封装过程中,无论是同步,还是异步,都有一小段代码是http状态码是200的时候,那一段代码重复了,需要封装成一个小函数,最终的封装如下:
function createXHR() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
return new ActiveXObject('MicroSoft.XMLHTTP');
}
}
function params(data) {
var arr = [];
for (var i in data) {
arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
}
return arr.join('&');
}
function ajax(obj) {
var xhr = createXHR();
obj.url = obj.url + '?rand=' + Math.random();
obj.data = params(obj.data);
if (obj.method == 'GET') {
obj.url += obj.url.indexOf('?') == -1 ? '?'+obj.data : '&'+obj.data;
console.log(obj.url);
}
xhr.open(obj.method, obj.url, obj.async);
if (obj.method == 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(obj.data);
} else {
xhr.send(null);
}
//异步处理
if (obj.async == true) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {//异步才有的readyState
callback();
}
}
}
//同步处理放在 open,send之后
if (obj.async == false) {
callback();
}
//无论同步异步,http都要判断状态码是否200,这一段共同代码,可以在ajax内部封装一个小函数
function callback() {
if (xhr.status == 200) {
obj.success(xhr.responseText);
} else {
alert('获取数据错误!错误代号:' + xhr.status + '错误信息:' + xhr.statusText);
}
}
}