Fork me on GitHub

Vue项目中axios的封装

前言

  • 在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。axios有很多优秀的特性,例如拦截请求和响应取消请求转换json客户端防御XSRF等。
  • 在一个完整的项目中,和服务端的交互会很频繁,一个项目会有很多请求,冗余代码很多。所以将请求封装,统一管理还是很有必要的。
  • 本文介绍的axios的封装主要目的就是在帮助我们简化项目代码利于后期的更新维护

axios的安装

  • 在项目中使用npm安装axios
    1
    npm install axios; // 安装axios

axios的封装

  • 一般在项目的src目录中会新建一个request文件夹,然后在里面新建一个http.js和一个api.js文件。http.js文件用来封装axios,而api.js用来统一管理接口
  • 代码示例:

    1
    2
    3
    4
    5
    // 在http.js中引入axios
    import axios from 'axios'; // 引入axios
    import QS from 'qs'; // 引入qs模块,用来序列化post类型的数据,后面会提到
    // vant的toast提示框组件,大家可根据自己的ui组件更改。
    import { Toast } from 'vant';
  • 接下来对封装axioshttp.js文件内容进行分析。

设置请求超时

  • 可以通过axios.defaults.timeout设置默认的请求超时时间。例如超过了10s,就会告知用户当前请求超时,请刷新等:
    1
    axios.defaults.timeout = 10000;

设置默认请求头

  • 可以在封装axios的文件中进行一个默认的设置,为post请求设置请求头:
    1
    axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

请求拦截

  • 发送请求前可以进行一个请求的拦截。比如,有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //http 请求拦截器
    axios.interceptors.request.use(
    config => {
    // 每次发送请求之前判断cookie中是否存在token
    const token = getCookie('token');// 注意使用的时候需要引入cookie方法,推荐js-cookie
    config.data = JSON.stringify(config.data);
    config.headers = {
    'Content-Type':'application/x-www-form-urlencoded'
    }
    // 如存在token,则在请求的header中携带token
    // if(token){
    // config.headers.Authorization = token;
    // }
    return config;
    },
    error => {
    return Promise.reject(err);
    }
    );

响应拦截

  • 与请求拦截类似,在拿到服务器返回给我们的数据前可以进行一个响应的拦截,从而进行我们想要的操作。如果后台返回的状态码是200,则正常返回数据,否则的根据错误的状态码类型进行一些我们需要的错误处理
  • 下面的例子主要是进行了错误的统一处理没登录或登录过期后调整登录页的操作:
    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
    // vant的toast提示框组件,大家可根据自己项目中使用的ui组件进行更改
    import { Toast } from 'vant';

    // 响应拦截器
    axios.interceptors.response.use(
    response => {
    if (response.status === 200) {
    // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
    return Promise.resolve(response);
    } else {
    // 否则的话抛出错误
    return Promise.reject(response);
    }
    },
    // 后台返回的状态码不是200
    // 这里可以跟后台开发人员协商好统一的错误状态码
    // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
    error => {
    if (error.response.status) {
    switch (error.response.status) {
    // 401: 未登录
    // 未登录则跳转登录页面,并携带当前页面的路径
    // 在登录成功后返回当前页面,这一步需要在登录页操作。
    case 401:
    router.replace({
    path: '/login',
    query: {
    redirect: router.currentRoute.fullPath
    }
    });
    break;
    // 403 token过期
    // 登录过期对用户进行提示
    // 清除本地token
    // 跳转登录页面
    case 403:
    Toast({
    message: '登录过期,请重新登录',
    duration: 1000,
    forbidClick: true
    });
    // 清除token
    removeCookie('token');
    // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
    setTimeout(() => {
    router.replace({
    path: '/login',
    query: {
    redirect: router.currentRoute.fullPath
    }
    });
    }, 1000);
    break;
    // 404请求不存在
    case 404:
    Toast({
    message: '网络请求不存在',
    duration: 1500,
    forbidClick: true
    });
    break;
    // 其他错误,直接抛出错误提示
    default:
    Toast({
    message: error.response.data.message,
    duration: 1500,
    forbidClick: true
    });
    }
    return Promise.reject(error.response);
    }
    }
    });

封装请求方法

  • 常用的ajax请求方法getpostput等方法,axios对应的也有很多类似的方法,具体可移步axios文档查看。前面提到为了简化我们的代码方便项目的后期维护,我们可以对axios的请求方法进行一个简单的封装。下面我们对getpost两个方法的封装进行举例。
get方法的封装
  • 我们通过定义一个get函数:
    • get函数有两个参数,第一个参数表示我们要请求的url地址,第二个参数是我们要携带的请求参数
    • get函数返回一个promise对象,当axios请求成功时resolve服务器返回值,请求失败时reject错误。最后通过export暴露get函数:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      /**
      * get方法,对应get请求
      * @param {String} url [请求的url地址]
      * @param {Object} params [请求时携带的参数]
      */
      export function get(url, queryParams){
      return new Promise((resolve, reject) =>{
      axios.get(url, {
      params: queryParams
      }).then(response => {
      resolve(response.data);
      }).catch(error =>{
      reject(error)
      })
      });
      }
post方法的封装
  • post的封装原理同get基本一样,但是要注意的是,post方法必须要使用对提交参数对象进行序列化的操作,所以这里我们通过nodeqs模块来序列化我们的参数(如果没有序列化操作,后台是拿不到我们提交的数据的)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /** 
    * post方法,对应post请求
    * @param {String} url [请求的url地址]
    * @param {Object} params [请求时携带的参数]
    */
    export function post(url, postData) {
    return new Promise((resolve, reject) => {
    axios.post(url, QS.stringify(postData))
    .then(res => {
    resolve(res.data);
    })
    .catch(err =>{
    reject(err.data)
    })
    }
  • 注意:axios.get()方法和axios.post()在提交数据时参数的书写方式还是有区别的,需要留意两者略微的区别:

    • get的第二个参数是一个{},然后这个对象的params属性值是一个参数对象。
    • post的第二个参数就是一个参数对象。

项目中引入http.js

  • 以vue项目为例,在项目的入口js文件main.js中引入封装好的axios文件(即http.js),以便在项目中使用已封装好的请求方法:

    1
    2
    3
    4
    import {post,fetch,patch,put} from './utils/http'
    //定义全局变量
    Vue.prototype.$post=post;
    Vue.prototype.$get=get;
  • 可以在组件里直接使用(以get方法为例):

    1
    2
    3
    4
    5
    6
    mounted(){
    this.$get('/api/getMenuList', {levels: 1})
    .then((response) => {
    console.log(response)
    })
    }
  • 然而,为了后期代码的可维护性,一般会新建一个api.js做统一的接口管理。把api统一集中起来,如果后期需要修改接口,我们就直接在api.js中找到对应的修改就好了,而不用去每一个页面都去查找我们的接口然后再修改。如果直接在我们的业务代码修改接口,一不小心还容易动到我们的业务代码造成不必要的麻烦。

为何不使用vue-resource的原因

  • Vue.js是数据驱动的,这使得我们并不需要直接操作DOM。jQuery主要用于简化dom操作,其中也封装了ajax方法,然而,如果我们不需要使用jQuery的DOM选择器,就没有必要引入jQuery。
  • vue-resource是Vue.js的一款插件,它可以通过XMLHttpRequest或JSONP发起请求并处理响应。也就是说,$.ajax能做的事情,vue-resource插件一样也能做到,而且vue-resource的API更为简洁。至于详细介绍请移步vue-resource文档。
  • 虽然vue-resource体积小、也基于promise的形式实现了各种http请求,包括各个环境的兼容处理,也有拦截器方面的功能。然而,vue2.0之后,就不再对vue-resource更新,而是推荐使用axios,这大概就是现大多项目使用的axios来做数据请求真正的原因吧。

参考文档

  1. axios官方文档
  2. axios封装和使用
  3. vue2.0 axios封装、vuex介绍
  4. vue中axios的封装
  5. vue-resource官方文档
  6. vue-resource对比axios
------ 本文结束 ------