vue中Axios的封装和API接口的管理

发表于:2019-5-14 10:43

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:愣锤    来源:掘金

分享:
   好了,最后把完成的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)
  })
  });
  }
   axios的封装根据需求的不同而不同,我也对此进行了思考和针对不同需求的改善。主要有以下改变:
   1.优化axios封装,去掉之前的get和post
   2.断网情况处理
   3.更加模块化的api管理
   4.接口域名有多个的情况
   5.api挂载到vue.prototype上省去引入的步骤
   http.js中axios封装的优化,先直接贴代码:/**
   * axios封装
  * 请求拦截、响应拦截、错误统一处理
  */
  import axios from 'axios';
  import router from '../router';
  import store from '../store/index';
  import { Toast } from 'vant';
  /**
  * 提示函数
  * 禁止点击蒙层、显示一秒后关闭
  */
  const tip = msg => {
  Toast({
  message: msg,
  duration: 1000,
  forbidClick: true
  });
  }
  /**
  * 跳转登录页
  * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
  */
  const toLogin = () => {
  router.replace({
  path: '/login',
  query: {
  redirect: router.currentRoute.fullPath
  }
  });
  }
  /**
  * 请求失败后的错误统一处理
  * @param {Number} status 请求失败的状态码
  */
  const errorHandle = (status, other) => {
  // 状态码判断
  switch (status) {
  // 401: 未登录状态,跳转登录页
  case 401:
  toLogin();
  break;
  // 403 token过期
  // 清除token并跳转登录页
  case 403:
  tip('登录过期,请重新登录');
  localStorage.removeItem('token');
  store.commit('loginSuccess', null);
  setTimeout(() => {
  toLogin();
  }, 1000);
  break;
  // 404请求不存在
  case 404:
  tip('请求的资源不存在');
  break;
  default:
  console.log(other);
  }}
  // 创建axios实例
  var instance = axios.create({    timeout: 1000 * 12});
  // 设置post请求头
  instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  /**
  * 请求拦截器
  * 每次请求前,如果存在token则在请求头中携带token
  */
  instance.interceptors.request.use(
  config => {
  // 登录流程控制中,根据本地是否存在token判断用户的登录情况
  // 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
  // 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
  // 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
  const token = store.state.token;
  token && (config.headers.Authorization = token);
  return config;
  },
  error => Promise.error(error))
  // 响应拦截器
  instance.interceptors.response.use(
  // 请求成功
  res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res),
  // 请求失败
  error => {
  const { response } = error;
  if (response) {
  // 请求已发出,但是不在2xx的范围
  errorHandle(response.status, response.data.message);
  return Promise.reject(response);
  } else {
  // 处理断网的情况
  // eg:请求超时或断网时,更新state的network状态
  // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
  // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
  store.commit('changeNetwork', false);
  }
  });
  export default instance;
  这个axios和之前的大同小异,做了如下几点改变:
   1.去掉了之前get和post方法的封装,通过创建一个axios实例然后export default方法导出,这样使用起来更灵活一些。
   2.去掉了通过环境变量控制baseUrl的值。考虑到接口会有多个不同域名的情况,所以准备通过js变量来控制接口域名。这点具体在api里会介绍。
   3.增加了请求超时,即断网状态的处理。说下思路,当断网时,通过更新vuex中network的状态来控制断网提示组件的显示隐藏。断网提示一般会有重新加载数据的操作,这步会在后面对应的地方介绍。
   4.公用函数进行抽出,简化代码,尽量保证单一职责原则。
   下面说下api这块,考虑到一下需求:
   1.更加模块化
   2.更方便多人开发,有效减少解决命名冲突
   3.处理接口域名有多个情况
   这里这里呢新建了一个api文件夹,里面有一个index.js和一个base.js,以及多个根据模块划分的接口js文件。index.js是一个api的出口,base.js管理接口域名,其他js则用来管理各个模块的接口。
   先放index.js代码:
   /**
  * api接口的统一出口
  */
  // 文章模块接口
  import article from '@/api/article';
  // 其他模块的接口……
  // 导出接口
  export default {
  article,
  // ……
  }
   index.js是一个api接口的出口,这样就可以把api接口根据功能划分为多个模块,利于多人协作开发,比如一个人只负责一个模块的开发等,还能方便每个模块中接口的命名哦。
   base.js:
   /**
  * 接口域名的管理
  */
  const base = {
  sq: 'https://xxxx111111.com/api/v1',
  bd: 'http://xxxxx22222.com/api'
  }
  export default base;
   通过base.js来管理我们的接口域名,不管有多少个都可以通过这里进行接口的定义。即使修改起来,也是很方便的。
   最后就是接口模块的说明,例如上面的article.js:
   /**
  * article模块接口列表
  */
  import base from './base'; // 导入接口域名列表
  import axios from '@/utils/http'; // 导入http中创建的axios实例
  import qs from 'qs'; // 根据需求是否导入qs模块
  const article = {
  // 新闻列表
  articleList () {
  return axios.get(`${base.sq}/topics`);
  },
  // 新闻详情,演示
  articleDetail (id, params) {
  return axios.get(`${base.sq}/topic/${id}`, {
  params: params
  });
  },
  // post提交
  login (params) {
  return axios.post(`${base.sq}/accesstoken`, qs.stringify(params));
  }
  // 其他接口…………
  }
  export default article;
   1.通过直接引入我们封装好的axios实例,然后定义接口、调用axios实例并返回,可以更灵活的使用axios,比如你可以对post请求时提交的数据进行一个qs序列化的处理等。
   2.请求的配置更灵活,你可以针对某个需求进行一个不同的配置。关于配置的优先级,axios文档说的很清楚,这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。
   3.restful风格的接口,也可以通过这种方式灵活的设置api接口地址。
   最后,为了方便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的原型上复制代码然后我们可以在页面中这样调用接口,eg:methods: {
  onLoad(id) {
  this.$api.article.articleDetail(id, {
  api: 123
  }).then(res=> {
  // 执行某些操作
  })
  }
  }
   再提一下断网的处理,这里只做一个简单的示例:
   <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)
  })
  }
   这是一种全局通用的断网提示,当然了,也可以根据自己的项目需求操作。具体操作就仁者见仁智者见智了。
   如果更多的需求,或者说是不一样的需求,可以根据自己的需求进行一个改进。

      上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号