现实项目中,常常会用到请求,但是在考虑低版本的浏览器时,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来处理,除了中间的=号,键和值都要处理,然后在末尾要添加随机数,防止缓存;