从其他角度去剖析AJAX

为什么会写这篇博客?
最近手上有一个项目,因为公司给的项目时间还是很宽松的,就想优化一下自己写的东西。因为交互引用的是axios库,本身其实没有任何问题,但是axios库太全了,我平时请求只需要get和post,对于强迫症的我来说,axios库过于臃肿了。

切入主题
所有web前端开发者都能熟悉使用第三方的库来和他们的后端做数据交互,但是他们中的很多对数据交互的概念本身不甚了解,这次我就从宏观角度来剖析一下,究竟什么才是交互。

数据怎么发出去又是如何接收到的?
    这是我刚做数据交互最有疑问的东西,事实上,我们可以用人类的行为来阐释类比这一过程。假设有个快递包裹,现在要将东西送达,需要什么?答案是,包裹,快递小哥,路,目标地址,还有最重要的,送出和收到的包裹是否一致。
    你发现了,包裹就是数据data,快递小哥就是浏览器通信接口(本次指的就是XHR),路就是数据传输媒介,目标地址就是url,确定一致性就是http协议,至此我们已经将整个过程抽象了出来:快递小哥拿到包裹,通过某些道路(不确定)将包裹送到目标地址处,签收人收到包裹后确定包括是否和说好的一致,一致就签收。
    回归到代码中就是

// 找个快递小哥[实例一个通信对象]
const xhr = new XMLHttpRequest();
// 是一封信?骑电动车送到xxx人家吧。是一个椅子?这得用汽车运啦。[初始化]
xhr.open('get','http://www.abc.com');
// 开始送快递。[发起请求]
xhr.send();
// 没送的时候告诉对方一声我没送,送的时候告诉对方送了,
// 对方拿到东西了,对方处理完了(无论好坏)告诉我们结果
xhr.onreadystatechange = function () {
    // 快递状态发生改变
    // do something...
}

在我们开始了解浏览器通信对象之前先吐槽一下它的破接口设计(所以才有了之后的fetch),为什么会有jQuery,axios等等第三方库?原因还不是因为这接口设计的一点都不优雅,在谷歌浏览器的控制台中输入 new XMLHttpRequest();

看到这个你有什么想法?啊,开发浏览器的大大们辛苦了。
    吐槽归吐槽,她长得再丑,也跟你在一起征战沙场很久了,老夫老妻了,难道说扔就扔不成?那肯定不成啊,我们得挖掘自己老婆的美(研究xhr对象),将她好好的打扮一下(封装)。
    xhr对象的核心:
    open()方法,此方法有好几个参数,我们只需要了解前三个(method,url,async),或者干脆一点了解前2个(method,url)。method就是请求类型,get还是post还是其他的,url就是你要请求的那个东西的地址,async接受布尔值默认为true,说实话,这玩意搞出来就是为了异步的,你改个参数让它变成同步的,接受它的返回值,那就不叫异步的js和xml了,不过有时候会有特殊需求,封装的时候还是带上这个哥们儿吧。
    setRequestHeader()方法,接受两个参数(key,value); 设置多个请求头就要多次调用这个方法。这哥们很多人不重视,这边我说个事儿吧,有一次我用axios向后台传了一波数据,他没接受到,因为axios默认的请求头是content-type:text/plain;我这边请求主体直接是一个payload丢过去的,因为服务端并没有判断我这个是啥类型的,默认以aa=bb&cc=dd的方式解析了我的数据。。。需要注意的是,这哥们调用的时候要在open()调用之后,send()之前,很容易理解,人家还没发呢你设置一下万一人家就是不发咋办?人家发出去了你设置一下跟你女神嫁给别人了你才来表白有区别?所以http协议了解一下?
    send()方法,这哥们调用的时候表示他要去发送数据了,注意的是如果用的是post,发送的数据得作为参数传进去,如果content-type是text/plain或者application/json就传一个json字符串,是application/x-www-form-urlencoded就传个'aa=bb&cc=dd'此类的字符串进去。
    onreadystatechange事件句柄,这个东西就跟dom.onclick一样,你可以理解成一个观察者,就像你身体在监视你体内的感冒病毒,一旦多了它就让你喉咙痛一痛,鼻涕流一流,意思是提醒你采取措施保护自己。你把一个函数交给onreadystatechange这哥们,他会在readyState属性发生改变的时候执行函数(采取措施)。
    readyState属性,这哥们在open()调用之前值是0,open()后就是1,send完收到部分信息就是2,下载数据主体时就变成3,接收信息完毕就是4,所以我们只需要在这哥们为4的时候搞事情。跟老婆要钱的时候你是乖宝宝,钱拿到手之后,对不起你就是上帝!
    status属性&statusText属性,这俩属性一一对应,status表示请求状态码,2xx的时候表示成功了(最常用的200,对应的statusText就是ok),3xx表示重定向,4xx表示前端有问题,5xx表示服务器有问题;
     好了,如果你完全把这些消化了,你就可以举一反三,好好打扮(封装)你的老婆(通信接口)了。
唉,写到这里,觉得xhr真的挺辛苦的,就像我的女友一样,每天下班回来都给我做饭。
最后贴上我封装的ajax:


// 对象转字符串
const toDataString = (object) => {
  let str = '';
  for (const key in object) {
    if (object.hasOwnProperty(key)) {
      if(object[key] && (object[key].constructor === Array || object[key].constructor === Object)){
        str += `${key}=${JSON.stringify(object[key])}&`; 
      } else {
        str += `${key}=${object[key]}&`;
      }
    }
  }
  return str.substring(0,str.length-1);
};

const request = ({
  url = '',
  type = 'get',
  timeout = 15000,
  async = true,
  data = {},
  // application/json 服务器老哥,json了解一下 
  // application/x-www-form-urlencoded 服务器老哥,'key=value&key2=value2'键值对你自己解析一下
  header = {'Content-Type':'application/x-www-form-urlencoded;charset=utf-8'}
} = {}) => {
  /* 初始化 */
  let 
    xhr = new XMLHttpRequest(), // 通信对象
    data_str = toDataString(data), // 数据的字符串形式
    req_url = '', // 请求地址 GET请求如需要传输数据则需要加上querystring
    send_data = null; // 发送的数据,GET不需要发送
  if (type.toLowerCase() === 'get') {
    req_url = `${url}?${data_str}`;
  } else if (type.toLowerCase() === 'post') {
    req_url = url;
    if (header['Content-Type'] === 'application/x-www-form-urlencoded;charset=utf-8') {
      send_data = data_str;
    } else {
      send_data = JSON.stringify(data);
    }
  } else {
    console.log('只能GET或者POST');
    return false;
  }
  /* 初始化 end */
  
  return new Promise((resolve,reject) => {
    // 监听readyState的变化 0 1 2 3 4  0和1不用去了解 2已经收到响应的一部分 3下载响应的主数据 4完事
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(JSON.parse(xhr.response));
        }
        if (xhr.status >= 400 || xhr.status < 600) {
          reject(xhr.status+xhr.statusText);
        }
      }
    }
    // 监听超时
    xhr.ontimeout = function () {
      console.log('超时,等会儿再试;');
    }
    // 打开信道定好目标
    xhr.open(type,`${req_url}`,async);
    // 设置头信息
    for (const key in header) {
      if (header.hasOwnProperty(key)) {
        xhr.setRequestHeader(key,header[key]);
      }
    }
    // 设置超时时间
    xhr.timeout = timeout; 
    // 发送
    xhr.send(send_data);
  });
};

request.get = (url,data,header) => {
  return request({
    url,
    data,
    header
  });
}

request.post = (url,data,header) => {
  return request({
    url,
    data,
    type:'post',
    header
  });
}

export default request;


     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值