Vue2总结(一)axios

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

趁着Vue3还没发布,先把vue2的各个知识点、源码、轮子全部温习一遍。
友情提示:阅读本文大概需要 30分钟

前言

axios 是基于 promise 用于浏览器和 node.js 是 http 客户端。axios的作用是什么呢:axios主要是用于向后台发起请求的,还有在请求中做更多是可控功能。特点是:支持浏览器和node.js、支持promise、能拦截请求和响应、能转换请求和响应数据、能取消请求、自动转换JSON数据、浏览器支持防止CSRF(跨站请求伪造)。下面一起复习一下axios的使用方法和 常用封装。

Vue2总结(一)axios

本文大纲

  • 简述Vue中的axios用法

  • 封装axios的

  • API接口的调用

  • 总结

Vue中的axios

Vue3尚未发布,而vue2.0以后,vue就不再对vue-resource进行更新,而是推荐axios,而大型项目都会使用 Vuex 来管理数据:

// 安装 axios
cnpm install axios  或 npm i axios 

axios请求方法:

  • axios.request(config)

  • axios.get(url[, config])

  • axios.post(url[, data[, config]])

  • axios.delete(url[, config])

  • axios.head(url[, config])

  • axios.options(url[, config])

  • axios.put(url[, data[, config]])

  • axios.patch(url[, data[, config]])


使用方式①:结合 vue-axios使用

看了vue-axios的源码,它是按照vue插件的方式去写的。那么结合vue-axios,就可以去使用vue.use方法了首先在主入口文件main.js中引用:

import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios,axios);

之后就可以使用了,在组件文件中的methods里去使用了:

getNewsList(){
      this.axios.get('api/getNewsList').then((response)=>{
        this.newsList=response.data.data;
      }).catch((response)=>{
        console.log(response);
      })
}
使用方式②:axios 改写为 Vue 的原型属性

首先在主入口文件main.js中引用,之后挂在vue的原型链上:

import axios from 'axios'
Vue.prototype.$ajax= axios

在组件中使用:

this.$ajax.get('api/getNewsList')
.then((response)=>{
    this.newsList=response.data.data;
}).catch((response)=>{
    console.log(response);
})

结合 Vuex的action在vuex的仓库文件store.js中引用,使用action添加方法:

import Vue from 'Vue'
import Vuex from 'vuex'

import axios from 'axios'

Vue.use(Vuex)
const store = new Vuex.Store({
  // 定义状态
  state: {
    user: {
      name'xiaoming'
    }
  },
  actions: {
    // 封装一个 ajax 方法
    login (context) {
      axios({
        method'post',
        url'/user',
        data: context.state.user
      })
    }
  }
})

export default store

在组件中发送请求的时候,需要使用 this.$store.dispatch:

methods: {
  submitForm () {
    this.$store.dispatch('login')
  }
}

axios使用方法

// 我们的项目结构
// 在 http.js里,引入axios
import axios from 'axios';
import QS from 'qs';

我们的项目环境可能有开发环境、测试环境和生产环境。我们通过node的环境变量来匹配我们的默认的接口url前缀。

// 环境的切换
if (process.env.NODE_ENV == 'development') {    
    axios.defaults.baseURL = 'https://www.baidu.com';} 
else if (process.env.NODE_ENV == 'debug') {    
    axios.defaults.baseURL = 'https://www.ceshi.com';

else if (process.env.NODE_ENV == 'production') {    
    axios.defaults.baseURL = 'https://www.production.com';
}
设置请求超时

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

axios.defaults.timeout = 10000;
post请求头的设置

post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置,即设post的请求头为application/x-www-form-urlencoded;charset=UTF-8。

axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-

urlencoded;charset=UTF-8'
;
拦截器

axios 一个重要的知识点就是拦截器,在请求或响应被 then 或 catch 处理前拦截它们(拦截器可以做什么:在请求或者响应时拦截下来进行处理)拦截器分为请求拦截器和响应拦截器。

请求拦截器(interceptors.requst)是指可以拦截每次或指定HTTP请求,并可修改配置项。响应拦截器(interceptors.response)可以在每次HTTP请求后拦截住每次或指定HTTP请求,并可修改返回结果项。

请求拦截

我们在发送请求前可以进行一个请求的拦截,为什么要拦截呢,我们拦截请求是用来做什么的呢?比如,有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。

// 先导入vuex,因为我们要使用到里面的状态对象
// vuex的路径根据自己的路径去写
import store from '@/store/index';

// 请求拦截器axios.interceptors.request.use(    
    config => {        
        // 每次发送请求之前判断vuex中是否存在token        
        // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
        const token = store.state.token;        
        token && (config.headers.Authorization = token);        
        return config;    
    },    
    error => {        
        return Promise.error(error);    
})
Token

这里插播一下 token,一般是在登录完成之后,将用户的 token 通过 localStorage 或者cookie 本地持久化,然后用户每次在进入页面的时候(即在入口文件main.js中),会首先从本地存储中读取 token,如果 token 存在说明用户已经登陆过,则更新 vuex 中的 token 状态。然后,在每次请求接口的时候,都会在请求的 header 中携带 token,后台人员就可以根据你携带的 token 来判断你的登录是否过期,如果没有携带,则说明没有登录过。

响应的拦截
// 响应拦截器
axios.interceptors.response.use(    
    response => {   
        // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据     
        // 否则的话抛出错误
        if (response.status === 200) {            
            return Promise.resolve(response);        
        } else {            
            return Promise.reject(response);        
        }    
    },    
    // 服务器状态码不是2开头的的情况
    // 这里可以跟你们的后台开发人员协商好统一的错误状态码    
    // 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
    // 下面列举几个常见的操作,其他需求可自行扩展
    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和清空vuex中token对象
                // 跳转登录页面                
                case 403:
                     Toast({
                        message'登录过期,请重新登录',
                        duration1000,
                        forbidClicktrue
                    });
                    // 清除token
                    localStorage.removeItem('token');
                    store.commit('loginSuccess'null);
                    // 跳转登录页面,并将要浏览的页面fullPath传过去登录成功后跳转需要访问的页面
 
                    setTimeout(() => {                        
                        router.replace({                            
                            path'/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);                    
                    break

                // 404请求不存在
                case 404:
                    Toast({
                        message'网络请求不存在',
                        duration1500,
                        forbidClicktrue
                    });
                    break;
                // 其他错误,直接抛出错误提示
                default:
                    Toast({
                        message: error.response.data.message,
                        duration1500,
                        forbidClicktrue
                    });
            }
            return Promise.reject(error.response);
        }
    }    
});

// 代码中的 Toast()方法,是引用的 ElementUI库的组件方法

可以简记成响应拦截器就是服务器返回给我们的数据,我们在拿到之前可以对它进行一些处理。例如上面的思想:如果后台返回的状态码是 200,则正常返回数据,否则的根据错误的状态码类型进行一些我们需要的错误,其实这里主要就是进行了错误的统一处理和没登录或登录过期后调整登录页的一个操作。


封装get方法和post方法

我们常用的 ajax 请求方法有 get、post、put等方法,相信小伙伴都不会陌生。axios 对应的也有很多类似的方法,不清楚的可以看下文档。但是为了简化我们的代码,我们还是要对其进行一个简单的封装。下面我们主要封装两个方法:get 和 post。get方法:我们通过定义一个 get 函数,get 函数有两个参数,第一个参数表示我们要请求的url地址,第二个参数是我们要携带的请求参数。get 函数返回一个 promise 对象,当axios其请求成功时resolve服务器返回 值,请求失败时 reject 错误值。最后通过 export 抛出 ge t函数。

// GET
/**
 * get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */

export function get(url, params){    
    return new Promise((resolve, reject) =>{        
        axios.get(url, {            
            params: params        
        }).then(res => {
            resolve(res.data);
        }).catch(err =>{
            reject(err.data)        
    })    
});}

// POST
/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */

export function post(url, params{
    return new Promise((resolve, reject) => {
         axios.post(url, QS.stringify(params))
        .then(res => {
            resolve(res.data);
        })
        .catch(err =>{
            reject(err.data)
        })
    });
}
//post方法必须要使用对提交从参数对象进行序列化的操作,
//所以这里我们通过node的qs模块来序列化我们的参数。

/** 
 * get方法,执行多个并发请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */

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

axios接口调用

新建一个api.js,然后在这个文件中存放我们所有的api接口。首先我们在api.js中引入我们封装的get和post方法。

/**   
 * api接口统一管理
 */

import { get, post } from './http'

比如我们现在有一个Post请求的接口:

http://www.baiodu.com/api/v1/users/my_address/address_edit_before

我们可以在api.js中这样封装:

export const apiAddress = p => post('api/v1/users/my_address/address_edit_before', p);

这里我们定义了一个 apiAddress 方法,这个方法有一个参数 p,p 是我们请求接口时携带的参数对象。而后调用了我们封装的 post 方法,post 方法的第一个参数是我们的接口地址,第二个参数是 apiAddress 的 p 参数,即请求接口时携带的参数对象。最后通过export 导出 apiAddress。然后在我们的页面中可以这样调用我们的 api 接口:

import { apiAddress } from '@/request/api';// 导入我们的api接口
export default {        
    name'Address',    
    created () {
        this.onLoad();
    },
    methods: {            
        // 获取数据            
        onLoad() {
            // 调用api接口,并且提供了两个参数                
            apiAddress({                    
                type0,                    
                sort1                
            }).then(res => {
                // 获取数据成功后的其他操作
                ………………                
            })            
        }        
    }
}
// 其他的api接口,就在pai.js中继续往下面扩展就可以了。

Api 接口管理的一个好处就是,我们把 api 统一集中起来,如果后期需要修改接口,我们就直接在api.js中找到对应的修改就好了,而不用去每一个页面查找我们的接口然后再修改会很麻烦。关键是,万一修改的量比较大,就规格gg了。还有就是如果直接在我们的业务代码修改接口,一不小心还容易动到我们的业务代码造成不必要的麻烦。下面是axios封装代码:

/**axios封装
 * 请求拦截、相应拦截、错误统一处理
 */

import axios from 'axios';import QS from 'qs';
import { Toast } from 'vant';
import store from '../store/index'

// 环境的切换
if (process.env.NODE_ENV == 'development') {    
    axios.defaults.baseURL = '/api';
else if (process.env.NODE_ENV == 'debug') {    
    axios.defaults.baseURL = '';
else if (process.env.NODE_ENV == 'production') {    
    axios.defaults.baseURL = 'http://api.123dailu.com/';
}

// 请求超时时间
axios.defaults.timeout = 10000;

// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-

urlencoded;charset=UTF-8'
;

// 请求拦截器
axios.interceptors.request.use(    
    config => {
        // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上

token,不用每次请求都手动添加了
        // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行

判断
        const token = store.state.token;        
        token && (config.headers.Authorization = token);        
        return config;    
    },    
    error => {        
        return Promise.error(error);    
    })

// 响应拦截器
axios.interceptors.response.use(    
    response => {        
        if (response.status === 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和清空vuex中token对象                
                // 跳转登录页面                
                case 403:                     
                    Toast({                        
                        message: '登录过期,请重新登录',                        
                        duration: 1000,                        
                        forbidClick: true                    
                    });                    
                    // 清除token                    
                    localStorage.removeItem('token');                    
                    store.commit('loginSuccess'null);                    
                    // 跳转登录页面,并将要浏览的页面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);        
        }       
    }
);

/** 
 * get方法,对应get请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */

export function get(url, params){    
    return new Promise((resolve, reject) =>{        
        axios.get(url, {            
            params: params        
        })        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {            
            reject(err.data)        
        })    
    });
}

/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */

export function post(url, params) {    
    return new Promise((resolve, reject) => {         
        axios.post(url, QS.stringify(params))        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {            
            reject(err.data)        
        })    
    });
}

总结

借用Vue官网的一段话,其实 Vue 和 axios 可以在一起配合的事情不只是访问和展示一个 API。你也可以和 Serverless Function 通信,向一个有写权限的 API 发送发布/编辑/删除请求等等。由于这两个库的集成很简单直接,它便成为了需要在工作流中集成 HTTP 客户端的开发者的常见选择。

最后

今天的 axios 就分享到这里,我的公众号没有留言功能哈,有问题大家心里默念,我能感受到,谢谢 ~ 

原文始发于微信公众号(程序员思语):Vue2总结(一)axios