$.ajax,axios,fetch三种ajax请求的区别、手写axios

1,829 阅读5分钟

ajax技术

Ajax技术 Ajax是常用的一门与Web服务器通信的技术,目前发送Ajax请求的主要有4种方式:

  • 原生XHR
  • jquery中的$.ajax()
  • axios
  • fetch

原生的XHR目前工作中已经很少去手写它了,前些年常用的是jqueryajax请求,但是近些年前端发展很快,jquery包装的ajax已经失去了往日的光辉,取而代之的是新出现的axiosfetch,两者都开始抢占“请求”这个前端重要领域。目前只需要熟练使用Axios就可以啦,Jqueryajax会逐渐被时代淘汰,Fetch虽然符合前端潮流,但是目前还尚不成熟,没有Axios使用起来便利。

Jquery ajax

$.ajax({
     type:"GET",
     url:url,
     data:data,
     dataType:dataType
     success:function(){},
     error:function(){}
})

ajaxjquery对原生XHR的封装,另外还增加了jsonp的支持,让ajax请求可以支持跨域请求,可以看这篇AJAX请求的五个步骤及步骤详解

但是要注意的是:jsonp请求本质不是XHR异步请求,就是请求了一个js文件,因此在浏览器的network面板中的xhr标签下看不到jsonp的跨域请求,但是在js标签下能看见。

  • 缺点(慢慢被抛弃的原因):
    • 要使用jquery ajax必须引入jquery整个大文件
    • jquery ajax本身是针对MVC设计模式的编程,与当前流行的基于MVVM模式的vue、react等框架不符合
    • jquery ajax本质是基于XHR的封装,而XHR本身架构不是很清晰,目前已采用fetch代替方案 总结

随着前端基于MVVM模式的VueReact新一代框架的兴起,以及ES6等新规范的制定,像Jquery这种大而全的JS库注定是要走向低潮的。

Axios

什么是Axios?

Axios是一个基于promiseHTTP库,它能够自动判断当前环境,自由切换在浏览器和node.js环境中。如果是浏览器,就会基于XMLHttpRequests实现;如果是node环境,就会基于node内置核心http模块实现。同时,它还用promise处理了响应结果,避免陷入回调地狱中去。

不仅如此,Axios还可以拦截请求和响应、转化请求数据和响应数据、中断请求、自动转换JSON数据、客户端支持防御XSRF等。

axios({
 method: 'post',
 url: '/login',
 data: {
     username:'jackson',
     password:'yyqx1128'
 }
})
.then(function (res) {
 console.log(res);
})
.catch(function (err) {
 console.log(err);
});

这种ajax请求方式是Vue框架的作者尤雨溪开始推荐使用的。其实Axios的本质也是基于原生XHR的封装,只不过它是基于ES6的新语法Promise的实现版本。并且具有以下几条特性:

  • 从浏览器中创建XHR
  • 从node.js中创建http请求
  • 支持promise API
  • 提供了并发请求的接口(重要,方便操作)
  • 支持拦截请求和响应
  • 支持取消请求
  • 客户端支持防御CSRF攻击

手写Axios核心原理

1.基本使用

axios基本使用方式主要有:

  • axios(config)
  • axios.method(url,data,config)
//以下是可用的实例方法。指定的配置将与实例的配置合并
axios#request(config)
axios#get(url[, config])
axios#delete(url[, config])
axios#head(url[, config])
axios#post(url[, data[, config]])
axios#put(url[, data[, config]])
axios#patch(url[, data[, config]])
// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    username: 'jackson',
    password:'yyqx1128'
  }
});
// GET请求ID参数
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

2.实现axios

从axios(config)的使用上可以看出导出的axios是一个方法,从axios.get()的使用上可以看出导出的axios原型上会有get,post,put,delete等方法。

由分析可知,axios实际上是Axios类中的一个方法。我们可以先写一个request方法来实现主要的请求功能,这样就能使用axios(config)形式来调用了。

class Axios{
  constructor(){
      ...
  }
  request(config){
    return new Promise((resove)=> {
      const {url='', data={}, method='get'} = config; //结构传参
      const xhr = new XMLHttpRequest;   // 创建请求对象
      xhr.open(method, url, true);
      xhr.onreadystatechange = () => {
        if(xhr.readyState == 4 && xhr.status == 200) {
          resove(xhr.responseText);
          // 异步请求返回后将Promise转为成功态并将结果导出
        }
      }
      xhr.send(data);
    })
  }
}
function CreateAxiosFn() {
  let axios = new Axios;
  let res = axios.request.bind(axios);
  return req;
}
let axios = CreateAxiosFn();

然后搭建一个简易服务端代码,以测试请求的效果:

const express = require('express')
 
let app = express();
 
app.all('*', function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    res.header('Access-Control-Allow-Methods', '*');
    res.header('Content-Type', 'application/json;charset=utf-8');
    next();
});
 
app.get('/getInfo', function(request, response){
    let data = {
        'username':'jackson',
        'age':'20',
    };
    response.json(data);
});
app.listen(3000, function(){
    console.log("服务器启动");
});

启动服务器后,在页面中测试请求是否成功:

<button onclick="getMsg()">点击</button>
<script src="./axios.js"></script>
<script>
    function getMsg(){
        axios({
            method: 'get',
            url: 'http://localhost:3000/getInfo'
        }).then(res => {
            console.log(res);
        })
    }
</script>

点击按钮后,可以看到请求成功并获取到数据。

总结

Axios除了和jquery ajax一样封装了原生的XHR,还提供了很多比如:并发请求、拦截等多种接口,同时它的体积还比较小,也没有下文fetch的各种问题,可以说是目前最佳的ajax请求方式了。

3.拦截器

在请求或响应被 then 或 catch 处理前拦截它们。

// -----------请求拦截--------
axios.intercepters.request.use(function(config) {
    // 在发送请求之前
    console.log('请求拦截:',config);  // 对数据的处理
    return config;
},function(error) {
    return Promise.reject(error);
})
// -----------响应拦截--------
axios.intercepters.response.use(function(response) {
    return response;
}, function(error) {
    return Promise.reject(error);
})

axios.interceptors.response.useaxios.interceptors.request.use来定义请求和响应的拦截方法。

拦截器,顾名思义就是在请求之前和响应之前,对真正要执行的操作数据拦截住进行一些处理。那么如何实现呢,首先拦截器也是一个类,用于管理响应和请求。

class InterceptorsManage {
    constructor(){
        this.handlers = [];
    }
    use(onFulField, onRejected) {
        // 将成功的回调和失败的回调都存放在队列中
        this.handlers.push({
          onFulField,
          onRejected
        })
    }
}

这说明axios上有响应拦截器和请求拦截器,那么如何在axios上实现呢:

class Axios{
    constructor(){
        this.interceptors = {
            request: new InterceptorsManage,
            response: new InterceptorsManage
        }
    }
    //....
}

在Axios的构造函数中新增interceptors属性,然后定义requestresponse属性用于处理请求和响应。

执行use方法时,会把传入的回调函数放到handlers数组中。

Fetch

try{
 var response=await fetch(url);
 var data=response.json();
 console.log(data);
}catch(e){
 console.log('error is'+e); 
}

上面说的$.ajax和Axios说到底本质都是对原生XHR的封装,但是Fetch可以说是新时代XHR的替代品。它的特性如下:

  • 更加底层,提供更丰富的API
  • 不基于XHR,是ES新规范的实现方式

但是目前Fetch还存在很多问题:

  • fetch只对网络请求报错,对400,500都当做成功的请求
  • fetch默认不会带cookie,需要添加配置项
  • fetch没有办法原生监测请求的进度,而XHR可以

Fetch在使用上说实话目前还没有axios和jquery ajax方便,但“跨域的处理”Fetch做的性能要远比XHR要好。因为同源策略的约束,浏览器发送的请求是不能随便跨域的,一定要借助JSONP或者跨域头来协助跨域,而Fetch可以直接设置mode为“no-cors”来实现跨域访问,如下所示:

fetch('/login.json', {
 method: 'post', 
 mode: 'cors',
 data: {name:martin''}
}).then(function() { /* handle response */ });

我们会得到一个type为“opaque”(透明)的response。这个请求是真正抵达过后台的,但是浏览器不可以访问返回的内容,这也就是为什么response中的type为“opaque”(透明)的原因。

fetch请求结果

总结 Fetch还是一个新时代的半成品,很多地方并不完善,但是也有它的优势所在,总的来说,就是Fetch技术还需要时间的沉淀,目前还没有达到axios的性能。

搬运文档1:$.ajax,axios,fetch三种ajax请求的区别

搬运文档2:手写Axios核心原理