关于axios

前言

Axios 相信对前端工程熟悉的铁汁对它不会感到陌生了,这简直就是前端近年来的一大杀器。它是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

特性

  • 从浏览器中创建 XMLHttpRequests
  • 从 node.js 创建 http 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF
    接下来我们就话不多说了,直接开始今天的主题,虽然axios很强,但是单纯的axios并不能满足我们日常的使用,因此很多时候我们都需要对axios进行二次封装,axios又是基于XMLHttpRequests的封装。套娃?yes

    为啥要封装axios

    随着项目规模的增大,每次发送请求,都需要设置超时时间、设置请求头、状态码的拦截、token是否携带、根据项目环境判断使用哪个请求,错误处理等等,重复的劳动浪费时间,还会让代码变得冗余,难以维护。那我们要做的有哪些呢?
  • POST请求参数序列化
  • 取消重复请求
  • Loading
  • 判断不同HTTP状态码
  • 请求自动携带token
  • 限制同时请求数
    废话不多说直接上代码
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
     let appId = "sdjhb-jdhoo-f265-djbhfj-iui"   //应用识别码,会在每一个url地址上拼接这个属性

    let stayTime = 3000 //设置zarmUI库Toast(轻提示)组件的停留时间

    /**
    * 设置超时时间和跨域是否允许携带凭证
    */
    axios.defaults.timeout = 10000; //10秒
    axios.defaults.withCredentials = true;

    /**
    * 设置post请求头
    * application/json;charset=UTF-8 JSON格式
    * application/x-www-form-urlencoded;charset=UTF-8 Form表单格式
    */
    axios.defaults.headers['Content-Type'] = 'application/json;charset=UTF-8';

    var CancelToken = axios.CancelToken;
    let sources = [] // 定义数组用于存储每个ajax请求的取消函数及对应标识

    /**
    * 请求防抖当一个url地址被请求多次就会取消前一次请求
    */
    let removeSource = (config) => {
    for (let source in sources) {
    // 当多次请求相同时,取消之前的请求
    if (sources[source].umet === config.url + '&' + config.method) {
    sources[source].cancel("取消请求")
    sources.splice(source, 1)
    }
    }
    }

    /**
    * 请求拦截器
    */
    axios.interceptors.request.use(config => {
    removeSource(config)
    config.cancelToken = new CancelToken((c) => {
    // 将取消函数存起来
    sources.push({ umet: config.url + '&' + config.method, cancel: c })
    })
    return config;
    }, error => {
    return Promise.reject(error)
    }
    )

    // 响应拦截器
    axios.interceptors.response.use(config => {
    if (config.data.statusCode >= 3000) {
    Toast.show({ content: config.data.msg, stayTime })
    }
    removeSource(config.config)

    return config.data;
    }, error => {
    if (!error.response) return
    switch (error.response.status) {
    // 401: 未登录
    // 未登录则跳转登录页面,并携带当前页面的路径
    // 在登录成功后返回当前页面,这一步需要在登录页操作。
    case 401:
    if (window.location.hostname === "localhost") {
    axios.post("/api/v1/login?client_name=form", {
    "userName": "lixiaoyao4_vendor",
    "password": 123456
    })
    } else {
    window.location = error.response.headers.locationurl;
    }
    break;

    // 403 token过期
    // 登录过期对用户进行提示
    // 清除本地token和清空vuex中token对象
    // 跳转登录页面
    case 403:
    Toast.show({ content: "登录过期,请重新登录", stayTime })
    // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
    if (window.location.hostname === "localhost") {
    axios.post("/api/v1/login?client_name=form", {
    "userName": "lixiaoyao4_vendor",
    "password": 123456
    })
    } else {
    window.location = error.response.headers.locationurl;
    }
    break;

    // 404请求不存在
    case 404:
    Toast.show({ content: "访问资源不存在", stayTime })
    break;
    // 其他错误,直接抛出错误提示
    default:
    Toast.show({ content: error.response.data.message, stayTime })
    }
    return Promise.reject(error.response)
    }
    )

    /**
    * get方法,对应get请求
    * @param {String} url [请求的url地址]
    * @param {Object} params [请求时携带的参数]
    */
    function get(url, params) {
    return new Promise((resolve, reject) => {
    axios.get(url, {
    params: {
    ...params,
    appId
    },
    }).then(res => {
    resolve(res);
    }).catch(err => {
    reject(err.data)
    })
    });
    }

    /**
    * post方法,对应post请求
    * @param {String} url [请求的url地址]
    * @param {Object} params [请求时携带的参数]
    */
    function post(url, params) {
    if (url.indexOf("?") === -1) {
    url += `?appId=${appId}`
    } else {
    url += `&appId=${appId}`
    }
    return new Promise((resolve, reject) => {
    axios.post(`${url}`, params)
    .then(res => {
    resolve(res);
    })
    .catch(err => {
    reject(err)
    })
    });
    }

    // 对外暴露
    export { post, get }

__END__