现实项目中,常常会用到请求,但是在考虑低版本的浏览器时,promise相关的axios,fetch这类第三方库的支持率就不那么好了,再考虑到最大的一个问题,跨域,更是让人头痛,虽然也有fetch-jsonp的方案,但是引入过多的库反而让项目变得复杂,后期插件版本升级等就比较繁琐。最常用的也是曾经红极一时的,那一定是jQuery或者zepto的$.ajax方案了,完美兼容多平台浏览器及低版本,支持jsonp跨域等,但是在现在大多数项目已不再用jQuery或zepto来实现开发,大多数用的是React,vue等前端库,所以如果仅仅为了一个ajax就引入一个库,未免有点浪费,所以,最好还是自己封装一个ajax函数,来模仿jQuery的$.ajax。
先看一下实现后的使用案例:
第一种,普通请求:
ajax({ url: 'test.php', // 请求地址 type: 'POST', // 请求类型,默认"GET",还可以是"POST" data: {'b': '异步请求'}, // 传输数据 success: function(res){ // 请求成功的回调函数 console.log(JSON.parse(res)); }, error: function(error) {} // 请求失败的回调函数 });
第二种,跨域请求:
ajax({ url: 'test', // 请求地址 jsonp: 'jsonpCallback', // 采用jsonp请求,且回调函数名为"jsonpCallbak",可以设置为合法的字符串 data: {'b': '异步请求'}, // 传输数据 success:function(res){ // 请求成功的回调函数 console.log(res); }, error: function(error) {} // 请求失败的回调函数 });
可以看出,都是用了ajax()这个方法,用法就跟jQuery的$.ajax一样,那如何实现?
不多说,直接上代码:
function ajax(params) { params = params || {}; params.data = params.data || {}; var json = params.jsonp ? jsonp(params) : json(params); } function json(params) { params.type = (params.type || 'GET').toUpperCase(); params.data = formatParams(params.data); var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4) { var status = xhr.status; if (status >= 200 && status < 300) { var type = xhr.getResponseHeader('Content-Type'); if (type.indexOf('xml') !== -1 && xhr.responseXML) { // document 对象响应 var response = xhr.responseXML; } else if (type == 'application/json') { // JSON响应 var response = JSON.parse(xhr.responseText); } else { // 字符串响应 var response = xhr.responseText; }
params.success && params.success(response);
} else { params.error && params.error(status); } } } if (params.type == 'GET') { xhr.open('GET', params.url + '?' + params.data, true); xhr.send(null); } else { xhr.open(params.type, params.url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); xhr.send(params.data); } } function jsonp(params){ var callbackName = params.jsonp; var head = document.getElementsByTagName('head')[0]; params.data['callback'] = callbackName; params.data = formatParams(params.data); var script = document.createElement('script'); head.appendChild(script); window[callbackName] = function(json){ head.removeChild(script); window[callbackName] = null; clearTimeout(script.timer); params.success && params.success(json) } script.url = params.url + '?' + params.data; // 设置超时限制 if (params.time) { script.timer = setTimeout(function(){ window[callbackName] = null; clearTimeout(script.timer); head.removeChild(script); params.error && params.error({ message: '超时' }); }, params.time); } } function formatParams(data) { var arr = []; for (var name in data) { arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name])) } // 添加随机数,防止缓存 arr.push('v=' + random()); return arr.join('&') } function random(){ return Math.floor(Math.random * 10000 + 500) }
这里有几个要注意的点:
1,参数为空的处理;
2,如果是非jsonp,在监听onreadyStateChange事件时,针对成功请求的结果,还要分情况处理返回值,根据Content-Type后面的值来讨论;
3,发送请求的时候,如果是非GET请求,需要设置请求头;
4,如果是jsonp,在调用回调的时候,一定要做内存清理、定时器清理等,保证性能;
5,数据格式化的时候,要用encodeURIComponent来处理,除了中间的=号,键和值都要处理,然后在末尾要添加随机数,防止缓存;
end