Axios概述

  • axios是一个基于Promise的http请求库
  • 可以用于浏览器和node.js,也就是说axios可以用于客户端和服务器端。
  • 不支持IE8以下的浏览器

Axios特性

  • 支持Promise API
  • 拦截请求和响应,即可以请求前和响应前做一些操作(比如说在请求头添加自定义的请求头)
  • 转换请求数据和响应数据,比如说在请求时对敏感信息进行加密,在响应时对敏感信息进行解密
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防御CSRF(跨站请求伪造)

Axios基础用法

axios返回的数据格式:

常见请求方法

get请求

// 方式1
axios.get('/data.json').then(data => {
  console.log(data);
});
// 方式2
axios({
  method: 'GET',
  url: '/data.json'
}).then(data => {
  console.log(data);
});
1
2
3
4
5
6
7
8
9
10
11

带参数的get请求,参数会以查询字符串的形式拼接在请求url后面。

// http://localhost:8080/data.json?id=12&name=lisi
axios.get('/data.json', {
  params: {
      id: 12,
      name: 'lisi'
  }
}).then(data => {
  console.log(data);
})
axios({
  method: 'GET',
  url: '/data.json',
  params: {
      id: 12,
      name: 'lisi'
  }
}).then(data => {
  console.log(data);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

post请求

post请求的数据格式有两种:

  1. form-data:表单提交,一般用于文件/图片上传
  2. application/json:大多数情况都是使用这种数据格式
const data = {
    id: 12,
    name: 'lisi'
};
axios.post('/create', data).then(data => {
    console.log(data);
});
axios({
    method: 'post',
    url: '/create',
    data
}).then(data => {
    console.log(data);
});
// formData格式的post请求
const formData = new FormData();
for (const key in data) {
    if (data.hasOwnProperty(key)) {
        formData.append(key, data[key]);
    }
}
axios.post('/create', formData).then(data => {
    console.log(data);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

需要注意:post请求的请求头中会比get请求多处一个字段:Content-Type: application/json;charset=UTF-8

put请求

axios.put('/update', data).then(data => {
    console.log(data);
});
1
2
3

patch请求

axios.patch('/update', data).then(data => {
    console.log(data);
})
1
2
3

需要注意:post、put、patch三个方法用法很类似,只不过从规范上来将作用不同。

delete请求

// http://localhost:8080/remove?id=12
axios.delete('/remove', {
    params: { // params是以查询字符串的形式拼接参数,请求头中没有Content-Type
        id: 12
    }
}).then(data => {
    console.log(data);
})
axios.delete('/remove', {
    // Content-Type: application/json;charset=UTF-8
    data: {
        id: 12
    }
}).then(data => {
    console.log(data);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

并发请求

并发请求:同时进行多个请求,并统一处理返回值

axios.all([
    axios.get('/data.json'), // 按照在数组中位置依次发送请求
    axios.get('/test.json')
]).then(
    axios.spread((dataRes, testRes) => {
        console.log(dataRes);
        console.log(testRes);
    })
);
1
2
3
4
5
6
7
8
9

Axios进阶用法

Axios实例

// axios实例
let instance = axios.create({
    baseURL: 'http://localhost:8080', // 请求域名,基本地址
    timeout: 1000, // 请求超时时长
    url: '/data.json', // 请求路径
    method: 'get', // 请求方法
    headers: { // 设置请求头
        token: ''
    },
    params: {}, // 请求参数拼接在url
    data: {} // 请求参数放在请求体中
});
// baseURL会被拼接在/data.json前面
instance.get('/data.json', data => {
    console.log(data);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Axios配置

// 1. axios全局配置
axios.defaults.timeout = 1000;
axios.defaults.baseURL = 'http://localhost:8080';
// 2. axios实例配置
let instance = axios.create();
instance.defaults.timeout = 3000; // 这里不设置的话,实例默认使用全局中的配置
instance.defaults.baseURL = 'http://localhost:8090';
// 3. axios请求配置
instance.get('/data.json', {
    timeout: 4000
});
1
2
3
4
5
6
7
8
9
10
11

优先级:axios请求配置 > axios实例配置 > axios全局配置。

// 实际开发
// 有两种请求接口
// http://localhost:8080
// http://localhost:8081
const instance = axios.create({
    baseURL: 'http://localhost:8080',
    timeout: 1000
});
const instance2 = axios.create({
    baseURL: 'http://localhost:8081',
    timeout: 3000
});
// baseURL,timeout,url,method,params
instance.get('/dataList', {
    params: {
        id: 12
    }
}).then(data => {
    console.log(data);
});
// baseURL,timeout: 5000,url,method
instance2.get('/userList', {
    timeout: 5000 // 覆盖instance2中设置的timeout
}).then(data => {
    console.log(data);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Axios拦截器

// 拦截器:在请求或响应被处理前进行拦截
// 分为请求拦截器和响应拦截器
// 请求拦截器
axios.interceptors.request.use(config => {
    // 在发送请求前做些事情
    return config;
}, err => {
    // 在请求错误的时候做些事情
    // 请求没有到达服务器
    return Promise.reject(err);
});
// 响应拦截器
axios.interceptors.response.use(res => {
    // 请求成功对响应数据做处理
    return res;
}, err => {
    // 在响应错误的时候做些事情
    return Promise.reject(err);
});
// 取消拦截器(了解)
const customInterceptors = axios.interceptors.request.use(config => {
    config.headers = {
        auth: true
    };
    return config;
});
// 对上述定义的拦截器进行取消
axios.interceptors.request.eject(customInterceptors);
// 例子:登录状态: token
let instance = axios.create({});
// 在实际开发中一般是个实例设置拦截器,如果直接给给axios设置的话会造成污染,因为axios是全局的。
// 可以用instance来访问需要登录的接口
instance.interceptors.request.use(config => {
    config.headers.token = 'token';
    return config;
});
// 可以用instance2来访问不需要登录的接口
let instance2 = axios.create({});
// 实例开发举例:在请求前打开弹窗,请求后取消弹窗
const instance_phone = axios.create({});
instance_phone.interceptors.request.use(confirm => {
    $('#dialog').show();
    return config;
});
instance_phone.interceptors.response.use(res => {
    $('#dialog').hide();
    return res;
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

取消请求

// 错误处理:在请求错误时进行的处理
axios.interceptors.request.use(config => {
    return config;
}, err => {
    return Promise.reject(err);
});
axios.interceptors.response.use(res => {
    return res;
}, err => {
    // 请求和响应拦截器中的错误都会被catch捕获到
    return Promise.reject(err);
});
axios.get('/data2.json').then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在实际开发过程中,一般在拦截器中添加统一的错误处理,对于特殊接口在catch单独处理。

const instance = axios.create({});
instance.interceptors.request.use(config => {
    return config;
}, err => {
    // 客户端请求错误,4xx
    $('#errorDialog').show();
    setTimeout(() => {
        $('#errorDialog').hide();
    }, 3000);
    return Promise.reject(err);
});
instance.interceptors.response.use(config => {
    return config;
}, err => {
    // 服务端响应错误,5xx
    $('#errorDialog').show();
    setTimeout(() => {
        $('#errorDialog').hide();
    }, 3000);
    return Promise.reject(err);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Axios封装

  • 第一步:将请求的api统一放到一个单独的js文件中,方便管理
  • 第二步:对请求方法进行统一格式的封装,包括参数传递、添加统一加载提示、在headers添加统一的token鉴权处理、错误处理。
const CONTACT_API = {
    // 获取联系人列表
    getContactList: {
        method: 'get',
        url: '/contactList'
    },
    // 新建联系人 form-data
    newContactForm: {
        method: 'post',
        url: '/contact/new/form'
    },
    // 新建联系人 application/json
    newContactJson: {
        method: 'post',
        url: '/contact/new/json'
    },
    // 编辑联系人
    editContact: {
        method: 'put',
        url: '/contact/edit'
    },
    // 删除联系人
    deleteContact: {
        method: 'delete',
        url: '/contact'
    }
};

export default CONTACT_API;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import axios from 'axios';
import {Toast} from 'vant';
import service from './contactApi';

// service 循环遍历输出不同的请求方法
const instance = axios.create({
    baseURL: 'http://localhost:9000/api',
    timeout: 1000
});
// 包裹请求方法的容器
const HttpClient = {};
for (const key in service) {
    if (service.hasOwnProperty(key)) {
        const api = service[key];
        // async:避免进入回调地狱
        HttpClient[key] = async function (
            params, // 请求参数 get/delete请求放到url中,put/post/patch放到请求体中
            isFormData=false, // 标识是否是form-data请求
            config={} // 配置参数,headers等
        ) {
            let newParams = {};
            if (params && isFormData) {
                newParams = new FormData();
                for (const key in params) {
                    if (params.hasOwnProperty(key)) {
                        newParams.append(key, params[key]);
                    }
                }
            } else {
                newParams = params;
            }
            // 不同请求判断
            let response = {}; // 请求的返回值
            if (api.method === 'post' || api.method === 'patch' || api.method === 'put') {
                try {
                    response = await instance[api.method](api.url, newParams, config);
                } catch (error) {
                    response = error;
                }
             } else if (api.method === 'get' || api.method === 'delete') {
                config.params = newParams;
                try {
                    response = await instance[api.method](api.url, config);
                } catch (error) {
                    response = error;
                }
             }
             return response;
        }
    }
}

// 拦截器的添加
instance.interceptors.request.use(config => {
    // 发送请求前做些事情
    Toast.loading({
        message: '加载中...',
        forbidClick: true,
        loadingType: 'spinner',
        duration: 0 // 提示一直存在
    });
    return config;
}, () => {
    Toast.clear(); // 清除加载中的提示
    Toast.fail('请求失败,请稍后重试');
});
// 响应拦截器
instance.interceptors.response.use(res => {
    Toast.clear();
    return res.data;
}, () => {
    Toast.clear(); // 清除加载中的提示
    Toast.fail('请求失败,请稍后重试');
});

export default HttpClient;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import HttpClient from './service/http'

Vue.config.productionTip = false
// 将HttpClient挂载到Vue实例上,这样就可以直接在页面中使用,不需要每次都引入了
Vue.prototype.$HttpClient = HttpClient
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
// 将HttpClient挂载到Vue实例上后,可以直接这样使用HttpClient
const res = await this.$HttpClient.editContact(info);
if (res.code === 200) {
    this.showEdit = false;
    Toast.success('编辑成功');
    this.getList();
}
1
2
3
4
5
6
7

参考文档

  1. vue中Axios的封装和API接口的管理
  2. https://mp.weixin.qq.com/s/P6HQqK13JIPP1IE43F5fgw