vue.js--Ajax(axios)的基础使用,axios封装与api接口管理

前言:vue.js 2.0版本推荐使用axios来完成ajax请求。Axios是一个基于Promise的HTTP库,可以用在游览器和node.js中。
axios的封装和api接口的统一管理,其实主要目的就是在帮助我们简化代码和利于后期的更新维护。
在vue项目中,和后台交互获取数据这块,通常使用axios库,它是基于promise的http库,可运行在游览器端和node.js中。它有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。
axios具有以下特性:

①从游览器中创建XMLHttpRequest;
②从node.js发出http请求;
③支持Promise API;
④拦截请求和响应;
⑤转换请求和响应数据;
⑥取消请求;
⑦自动转换JSON数据;
⑧客户端支持防止CSRF/XSRF。

一、安装方法
1.使用CDN:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2.使用npm:

$ npm install axios

3.使用bower:

$ bower install axios

4.使用yarn:

$ yarn add axios

二、axios封装步骤
引入
一般,在项目的src目录中,新建一个network文件夹,作为我们的网络请求模块,然后,在里面新建一个http.js和一个api.js文件和一个request.js;http.js文件用来封装我们的axios,app.js用来统一管理我们的接口url,request.js对外暴露我们放在的api方法。

//在http.js中引入axios
import axios from 'axios';  //引入axios
import QS from 'qs'; // 引入qs模块,用来序列化post类型的数据,后面会提到
import router from '../router';
import { Message } from "element-ui";
import store from "@/store";
import { getToken, setToken } from "@/utils/auth";

环境的切换
我们项目的环境可能有开发环境、测试环境和生产环境。通过node的环境变量来匹配,我们默认的接口url前缀。axios.defaults.baseURL可以设置axios的默认请求地址。
创建config目录:
目录下创建env.development.js和env.production.js+env.test.js
en.development.js内容如下:

module.exports={
	baseUrl:'http://www.....com'  //开发环境用到的baseUrl
}
//环境的切换
const {baseUrl}=require('../config/env.'+process.env.NOE_ENV);

//同时package.json的script需指定测试环境的模式 --mode test

"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "test": "vue-cli-service build --mode test",
    "lint": "vue-cli-service lint"
  }

const service = axios.create({
  baseURL: baseUrl, // url = base api url + request url
  withCredentials: false, // send cookies when cross-domain requests
  timeout: 1000*12 // 请求超时
})

如上,设置请求超时
设置请求超时
通过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';
  • 请求拦截
    我们在发送请求前,可以进行一个请求的拦截,为什么要拦截呢,我们拦截的请求时用来做什么的呢?比如,有些请求时需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。
    请求拦截
//创建实例
// create an axios instance
const service = axios.create({
  baseURL: `${process.env.VUE_APP_BASE_API}`,
  timeout: 10000,
  headers: {
    "X-Requested-With": "XMLHttpRequest",
    "Content-Type": "application/json;charset=utf-8"
  }
});
//先导入vuex,因为我们要使用到里面的状态对象
import store from "@/store";
import { getToken, setToken } from "@/utils/auth";
//请求拦截器
// request interceptor
service.interceptors.request.use(
  config => {
    const url = config.url.split(config.baseURL).join("");
    const token = getToken();

    if (whiteUrl.indexOf(url) < 0) {
      if (config.url.indexOf('cmp') < 0) {
        config.url = `${config.baseURL}/${store.getters["app/productItem"]["api"]}${url}`;
      }
    }
    //每次发送请求之前,判断token是否存在
    //如果存在,则统一在http请求的header都加上token,这样后台根据tokne判断你的登录情况
    //即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
    if (token) {
      config.headers["authorization"] = token;
    }
    return config;
  },
  error => {
    // do something with request error
    // console.log(error); // for debug
    return Promise.reject(error);
  }
);

这里说明一下token,一般是在登录完成之后,将用户的token通过localStorage或者cookie存在本地,然后用户每次进入页面的时候(即在main.js中),会首先从本地存储中读取token,如果token存在说明用户已经登录过,则更新token的状态。然后,在每次请求接口的时候,都会在请求的header中携带token,后台人员就可以根据你携带的token来判断你的登陆是否过期,如果没有携带,则说明没有登录过。这时候或许有些小伙伴会有疑问了,就是每个请求都携带token,那么要是一个页面不需要用户登录就可以访问的怎么办呢?其实,前端的请求可以携带token,但是后台可以选择不接收啊。
补充一下auth.js中获取token的方法

import Cookies from 'js-cookie'

const TokenKey = 'lanshan_token'

const UserInfoKey = 'lanshan_user'

const ProductKey = 'product_key'

const ExpiresTime = 1 / 4

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token) {
  return Cookies.set(TokenKey, token, {
    expires: ExpiresTime
  })
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

export function getUserName() {
  return Cookies.get(UserInfoKey)
}

export function setUserName(name) {
  return Cookies.set(UserInfoKey, name, {
    expires: ExpiresTime
  })
}

export function removeUserName() {
  return Cookies.remove(UserInfoKey)
}

export function getProductKey() {
  return Cookies.get(ProductKey)
}

export function setProductKey(name) {
  return Cookies.set(ProductKey, name, {
    expires: ExpiresTime
  })
}

export function removeProductKey() {
  return Cookies.remove(ProductKey)
}

响应的拦截

// 响应拦截器
service.interceptors.response.use(
  response => {
    // console.log("response", response);
    const res = response.data;
    const code = Number(res.code);
    let baseUrl = `${response.config.baseURL}`;

    if (response.config.url.indexOf('cmp') < 0) {
      baseUrl = `${response.config.baseURL}/${store.getters["app/productItem"]["api"]}`;
    }

    const downloadUrl = response.config.url.split(baseUrl).join("");

    if (downloadUrlList.indexOf(downloadUrl) >= 0) {
      return response;
    }
    //与后台开发人员协商好统一的错误状态码,根据返回的状态码进行一些操作,例如登录过期,错误提示等...
    if (code !== 0) {
      if (code === 401) {//登录失效,退出登录
        Message({
          message: "登录失效,请重新登录",
          type: "error",
          duration: 3 * 1000
        });
        store.dispatch("user/logout").then(() => {
          location.reload();
        });
      }else if (code === 403) {//403token过期
        Message({
          message: "登录过期,请重新登录",
          type: "error",
          duration: 3 * 1000
        });
        store.dispatch("user/logout").then(() => {
          location.reload();
        });
        // 清除本地token和清空vuex中token对象
		localStorage.removeItem('token');
		store.commit('loginSuccess', null);
      }else {
        Message({
          message: res.message || "接口请求出错",
          type: "error",
          duration: 3 * 1000
        });
      }
      return Promise.reject(new Error(res.message || "接口请求出错"));
    }
    if (response.headers.authorization) {
      setToken(response.headers.authorization);
    }
    return res;
  },
  error => {
    const originalRequest = error.config;

    if (error.code === "ECONNABORTED" && error.message.indexOf("timeout") !== -1 && !originalRequest._retry) {
      Message({
        message: "网络开小差了~",
        type: "error",
        duration: 5 * 1000
      });
      return Promise.reject("网络开小差了~");
    }
    Message({
      message: error,
      type: "error",
      duration: 5 * 1000
    });
    return Promise.reject(error);
  }
);

export default service;

响应拦截器很好理解,就是服务器返回给我们的数据,我们在拿到之前可以对它进行一些处理。如:如果后台返回的状态码是200,则正常返回数据,否则,根据错误的状态码类型进行一些我们需要的错误,其实这里主要就是进行了错误的统一处理没登录,或登录过期后调整登录页的一个操作。
封装get方法和post方法
我们常用的ajax请求方法有get、post、put等方法。axios对应的也有很多类似的方法,为了简化代码,我们可以对其进行一个简单的封装。我们主要封装两个方法:get和post。
get方法:我们通过定义一个get函数,get函数有两个参数,第一个参数表示,我们要请求的url地址,第二个参数是我们要携带的请求参数。get函数返回一个promise对象,当axios其请求成功时,resolve服务器返回值,请求失败时reject错误值。最后通过export抛出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方法:原理同get基本一样,但是,post方法必须要使用对提交参数对象进行序列化的操作,所以这里我们通过node的qs模块来序列化我们的参数。这个很重要,如果没有序列化操作,后台是拿不到你提交的数据的。

/**
* 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)
			})
	});
}

axios.get()方法和axios.post()在提交数据时,参数的书写方式还是有区别的。区别就是,get的第二个参数是一个{},然后,这个对象的params属性值是一个参数对象的。而post的第二个参数就是一个参数对象。
三、api接口的统一管理
新建了一个api文件夹,里面有一个index.js,以及多个根据模块划分的接口js文件。index.js是一个api的出口,其他js则用来管理各个模块的接口。
例如:下面的article.js

/**
 * article模块接口列表
 */
 import request from '@/netword/http'; //导入http中创建的axios实例
 import qs from 'qs'; //根据需求是否导入qs模块

const article = {    
    // 新闻列表    
    articleList () {        
       return request({
       url: '/artical',
       method: 'get',
       params,
    })  
    },    
    // 新闻详情,演示    
    articleDetail (id, params) {        
         return request({
		      url: '/detail',
		      method: 'get',
		      params:{
		        goodsId
		      },
		    })
    },
    // post提交    
    login (data) {        
      return request({
      url:'/adduser',
      method:'post',
      data:qs.stringify(data), //注意post提交用data参数
     })   
    }
    // 其他接口…………
}
 
export default article;

index.js代码:

/** 
 * api接口的统一出口
 */
// 文章模块接口
import article from '@/api/article';
// 其他模块的接口……
 
// 导出接口
export default {    
    article,
    // ……
}

在组件中的使用(按需导入)

import {article} from '@/api/index'

created(){
   article.articleList().then(info=>{
       if(info.code==200){
     		this.num=info.data
  		}
     })
}

api挂载到vue.prototype上省去引入的步骤
为了方便api的调用,我们需要将其挂载到vue的原型上。在main.js中:

import Vue from 'vue'
import App from './App'
import router from './router' // 导入路由文件
import store from './store' // 导入vuex文件
import api from './api' // 导入api接口
 
Vue.prototype.$api = api; // 将api挂载到vue的原型上复制代码

然后,我们在组件中可以这么用:

//无需导入
methods: {    
    onLoad(id) {      
        this.$api.article.articleDetail(id, {        
            api: 123      
        }).then(res=> {
            // 执行某些操作      
        })    
    }  
}

断网情况处理
如下app.vue新增:

<template>  
    <div id="app">    
        <div v-if="!network">      
            <h3>我没网了</h3>      
            <div @click="onRefresh">刷新</div>      
        </div>    
        <router-view/>      
    </div>
</template>
 
<script>
    import { mapState } from 'vuex';
    export default {  
        name: 'App',  
        computed: {    
            ...mapState(['network'])  
        },  
        methods: {    
            // 通过跳转一个空页面再返回的方式来实现刷新当前页面数据的目的
            onRefresh () {      
                this.$router.replace('/refresh')    
            }  
        }
    }
</script>

这是app.vue,这里简单演示一下断网。在http.js中介绍了,我们会在断网的时候,来更新vue中network的状态,那么这里我们根据network的状态来判断是否需要加载这个断网组件。断网情况下,加载断网组件,不加载对应页面的组件。当点击刷新的时候,我们通过跳转refesh页面然后立即返回的方式来实现重新获取数据的操作。因此我们需要新建一个refresh.vue页面,并在其beforeRouteEnter钩子中再返回当前页面。

// refresh.vue
beforeRouteEnter (to, from, next) {
    next(vm => {            
        vm.$router.replace(from.fullPath)        
    })    
}

参考博客:
axios封装与api接口管理 https://zhuanlan.zhihu.com/p/262665309
vue中Axios的封装和API接口的管理 https://zhuanlan.zhihu.com/p/135965633
Vue之axios基础使用 https://www.jianshu.com/p/4ee31fdb78b6
Vue.js Ajax(axios) https://www.runoob.com/vue2/vuejs-ajax-axios.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值