1.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中控制着一个全局的断网提示组件的显示隐藏 // 关于断网组件中的刷新重新获取数据,会在断网组件中说明 if (!window.navigator.onLine) { store.commit('changeNetwork', false); } else { return Promise.reject(error); } } }); export default instance;
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>
beforeRouteEnter
钩子中再返回当前页面。// refresh.vue beforeRouteEnter (to, from, next) { next(vm => { vm.$router.replace(from.fullPath) }) }
这是一种全局通用的断网提示,当然了,也可以根据自己的项目需求操作。具体操作就仁者见仁智者见智了。
如果更多的需求,或者说是不一样的需求,可以根据自己的需求进行一个改进。
链接:https://juejin.im/post/5b55c118f265da0f6f1aa354
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。