首发于 前端之旅

一文秒懂 ajax, fetch, axios

1, JavaScript的Ajax

Ajax的全称是Asynchronous JavaScript and XML,意思就是用JavaScript执行异步网络请求,而不需要重载(刷新)整个页面。

Ajax使用XMLHttpRequest对象取得新数据,然后再通过 DOM 将新数据插入到页面中。另外,虽然名字中包含 XML 的成分,但 Ajax 通信与数据格式无关; 这种技术就是无须刷新页面即可从服务器取得数据,但不一定是 XML 数据。

对于IE7+和其他浏览器,可以直接使用XMLHttpRequest对象,对于IE6及以前的浏览器,使用ActiveXObject对象。

使用方法如下:

var xhr;
if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
} else {
    xhr = new ActiveXObject('Microsoft.XMLHTTP');
}

启动请求:

xhr.open(method, url, boolean);     
xhr.send();

注:

1,xhr.open参数含义:

method:请求方式,post、get等

url: 请求链接,只能向同源的url发送请求

boolean:是否异步请求,true:异步, false: 同步,默认为true

2,调用 open()方法并不会真正发送请求, 而只是启动一个请求以备发送。

3,send()方法接收一个参数,即要作为请求主体发送的数据(post方法会使用,get方法直接传null)。如果不需要通过请求主体发送数据,则必须传入 null,因为这个参数对有些浏览器来说是必需的。调用send()之后,请求就会被分派到服务器。

XMLHttpRequest对象的异步请求示例如下:

function success(text) {
    console.log(text);
function fail(code) {
   console.log(code);
var xhr = new XMLHttpRequest();     // 新建XMLHttpRequest对象
xhr.onreadystatechange = function () { 
    // 状态发生变化时,函数被回调
    if (xhr.readyState === 4) { // 成功完成
        // 判断响应结果:
        if (xhr.status === 200) {
            // 成功,通过responseText拿到响应的文本:
            return success(xhr.responseText);
        } else {
            // 失败,根据响应码判断失败原因:
            return fail(xhr.status);
    } else {
        // HTTP请求还在继续...
// 发送请求:
xhr.open('get', '/api/categories');
xhr.send(null);

xhr的属性含义如下:

responseText: 作为响应主体被返回的文本。

responseXML: 如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存响应数据的 XML DOM 文档。

status: 响应的 HTTP 状态。

statusText: HTTP 状态的说明。

readyState :表示请求/响应过程的当前活动阶段。可取值如下。

0: 未初始化。尚未调用 open()方法。

1: 启动。已经调用 open()方法,但尚未调用 send()方法。

2: 发送。已经调用 send()方法,但尚未接收到响应。

3: 接收。已经接收到部分响应数据。

4: 完成。已经接收到全部响应数据,而且已经可以在客户端使用了。

只要 readyState 属性的值由一个值变成另一个值,都会触发一次 readystatechange 事件。可以利用这个事件来检测每次状态变化后readyState 的值。通常,我们只对 readyState 值为 4 的阶段感兴趣,因为这时所有数据都已经就绪。不过,必须在调用 open()之前指定 onreadystatechange事件处理程序才能确保跨浏览器兼容性。

另外,在接收到响应之前还可以调用 abort()方法来取消异步请求:

xhr.abort();

调用这个方法后,XHR 对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性。在终止请求之后,还应该对 XHR 对象进行解引用操作。由于内存原因,不建议重用 XHR 对象。

2, jQuery的Ajax

$.ajax({
        url:"",
        type:"GET",
        contentType: '',
        async:true,
        data:{},
        dataType:"",
        success: function(){
});

url 必填项,规定把请求发送到哪个 URL。

type 以什么样的方式获取数据,是get或post

contentType:发送POST请求的格式,默认值为’application/x-www-form-urlencoded;

charset=UTF-8’,也可以指定为text/plain、application/json

async 是否异步执行AJAX请求,默认为true,千万不要指定为false

data 发送的数据,可以是字符串、数组或object。如果是GET请求,data将被转换成query附加到URL上,如果是POST请求,根据contentType把data序列化成合适的格式;

dataType

接收的数据格式,可以指定为’html’、‘xml’、‘json’、'text’等,缺省情况下根据响应的Content-Type猜测。

success 可选。执行成功时返回的数据。

缺点:

是基于XHR原生开发的,目前已有的fetch可替代。本身是针对mvc的编程模式,不太适合目前mvvm的编程模式。jQuery本身比较大,如果单纯的使用ajax可以自己封装一个,不然会影响性能体验。

3,Axios

Vue2.0之后,axios开始受到更多的欢迎。其实axios也是对原生XHR的一种封装,不过是Promise实现版本。它是一个用于浏览器和 nodejs 的 HTTP 客户端,符合最新的ES规范。

axios具有以下特征:

  1. 从浏览器中创建 XMLHttpRequest
  2. 支持 Promise API
  3. 客户端支持防止CSRF
  4. 提供了一些并发请求的接口
  5. 从 node.js 创建 http 请求
  6. 拦截请求和响应
  7. 转换请求和响应数据
  8. 取消请求
  9. 自动转换JSON数据

PS:防止CSRF:就是让你的每个请求都带一个从cookie中拿到的key, 根据浏览器同源策略,假冒的网站是拿不到你cookie中得key的,这样,后台就可以轻松辨别出这个请求是否是用户在假冒网站上的误导输入,从而采取正确的策略。

设置全局的 axios 默认值

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'

注:axios 的 headers的 content-type 默认是 “application/json ”

默认情况下,axios将JavaScript对象序列化为JSON,如果是get请求,对请求参数不用做任何处理,但是如果是post请求,并且Content-Type 为application/x-www-form-urlencoded,需要使用URLSearchParams API格式化请求参数, 否则Content-Type依然是application/json

var params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');

get请求,以下3中写法完全等价

// 第一种写法
axios.get('/user?id=12345&name=xiaoming')
.then(function (response) {
    console.log(response);
.catch(function (error) {
    console.log(error);
// 第二种写法
axios.get('/user', {
    params: {
      id: '12345',
      name: 'xiaoming'
.then(function (response) {
    console.log(response);
.catch(function (error) {
    console.log(error);
// 第三种写法
axios({
    url: '/user',
    method: 'get',
    params: {
      id: '12345',
      name: 'xiaoming'
.then(function (response) {
    console.log(response);
.catch(function (error) {
    console.log(error);
});

post请求,以下2种写法完全等价

// 第一种写法
axios({
    url: '/user',
    method: 'post',
    headers: {
        'Content-Type': 'application/json'
    data: {
      id: '12345',
      name: 'xiaoming'
.then(function (response) {
    console.log(response);
.catch(function (error) {
    console.log(error);
// 第二种写法
var url = '/user';
var data = {
      id: '12345',
      name: 'xiaoming'
axios.post(url, data, {
       headers: {
        'Content-Type': 'application/json'
.then(function (response) {
    console.log(response);
.catch(function (error) {
    console.log(error);
});

执行多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
function getUserPermissions() {
  return axios.get('/user/12345/permissions');
axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
}));

创建实例

可以使用自定义配置新建一个 axios 实例

axios.create([config])

var instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

配置会以一个优先顺序进行合并,顺序由低到高为

1,在 node_modules/axios/lib/defaults.js 找到的库的默认值

2,实例的 defaults 属性

3,请求的 config 参数

// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
var instance = axios.create();
// 覆写库的超时默认值
// 现在,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;
// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
  timeout: 5000
});

拦截器

在请求发出之前或响应被 then 或 catch 处理前拦截它们做预处理。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
  }, function (error) {
    // 对请求错误做些什么
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
  }, function (error) {
    // 对响应错误做点什么
  });

可以在稍后移除拦截器:

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

可以为自定义 axios 实例添加拦截器

var instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

4, fetch

window 自带了 window.fetch 方法, 在最新版的 Firefox 和 Chrome 中已经提供支持,其他浏览器还有兼容性问题,要做兼容性处理。fetch 是一个 基于promise设计的low-level API,不是ajax的进一步封装,而是原生js,它注定不会像你习惯的 $.ajax 或是 axios 等库帮你封装各种各样的功能或实现.

interface GlobalFetch {
    fetch(input: RequestInfo, init?: RequestInit): Promise<Response>;
}

fetch()是一个全局的函数,返回一个promise对象。它有两个参数,第一个参数是请求的地址,第二个参数是可选,RequestInit是个对象格式如下:

interface RequestInit {
    body?: any;
    cache?: RequestCache;
    credentials?: RequestCredentials;
    headers?: HeadersInit;
    integrity?: string;
    keepalive?: boolean;
    method?: string;
    mode?: RequestMode;
    redirect?: RequestRedirect;
    referrer?: string;
    referrerPolicy?: ReferrerPolicy;
    window?: any;
}

优点:

符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里

更好更方便的写法

更加底层,提供的API丰富

脱离了XHR,是ES规范里新的实现方式

fetch中可以设置mode为"no-cors"(不跨域)

缺点:

fetch不支持同步请求

fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理

fetch默认不会带cookie,需要添加配置项

fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费

fetch没有办法原生监测请求的进度,而XHR可以

fetch的使用示例:

window.fetch(url)
    .then(response => {
        if (response.ok) {
            //通过 response 原型上的 json 方法将 response.body 转换为 JS 对象,再返回出去
            return response.json();