以前在一家面试时,面试官问及vue中使用ajax请求时我是如何作的,初学者在初学vue时或者说刚上手的开发者,会有人使用过vue-resource(事实上已经被官方抛弃的一个库,如今都推荐使用axios)或aioxs,可是大多不例外的都是在每一个组件中直接去调用这些库的api来进行请求,当时我第一次作vue项目的公司也是这么干的,面试官跟我说这样或许没有问题,可是不够规范也不能方便的管理全部请求的参数配置。
最近的一个vue项目是我本身建立的,因而便想到了此事,结果也正如我所想,确实涉及到许多关于请求参数上的统一配置好比token,用户id等须要传递给后台校验的参数,若是咱们是在每个请求中去传递,项目动辄几十个或上百个的请求,一个个的去添加那是很是麻烦且傻气的作法。
首先看看个人文件结构,我建立了一个http.js,这是我用来专门二次封装axios和管理全局请求的一个文件,router.js配置路由的文件,main.js是vue的入口文件,咱们主要靠这三个文件实现路由访问控制。vue
http.js的内容其实也很是的简单ios
import Vue from 'vue'; import axios from 'axios'; import qs from 'qs'; import router from './router'; import md5 from 'js-md5'; let vm = new Vue(); axios.defaults.timeout = 60000;// 在超时前,全部请求都会等待60 秒
首先先使用请求拦截器,对发送的请求体作相应的配置,好比向请求头添加一些必须的参数,或许会用于后台的身份验证如token等,一些接口的加密方法,也能够在请求时统一的显示loading面试
function getHeaderKey(){// 一个加密的方法} axios.interceptors.request.use( config => { // config.data = JSON.stringify(config.data); if (localStorage.token) { // 判断是否存在token,若是存在的话,则每一个http header都加上token config.headers["X-Access-Token"] = `${localStorage.token}`; // 加载loading vm.$loading(); } // 接口加密验证 config.headers["channelType"] = 'wx_applet'; config.headers["for_valid"] = getHeaderKey(); return config; }, err => { // 关闭loading vm.$loading.close(); return Promise.reject(err); });
接下来使用响应拦截器对响应体返回的一些参数进行判断,好比接口是否返回了token失效等问题,以作统一的管理。token失效的状态码,须要和后台去约定,并不是必定是401,甚至能够在下面具体封装的post或get等方法中去判断。ajax
// http response 拦截器 axios.interceptors.response.use( response => { // 关闭loading vm.$loading.close(); return response; }, error => { // 关闭loading vm.$loading.close(); if (error.response) { vm.$toast.center("返回code:" + error.response.status + ';'+'请求错误'); switch (error.response.status) { case 401: // 返回 401 清除token信息并跳转到登陆页面 localStorage.removeItem('token'); localStorage.clear(); router.replace({ path: 'login', query: {redirect: router.currentRoute.fullPath} }); break; } } return Promise.reject(error) // 返回接口返回的错误信息 });
接下来封装具体的请求方法,能够对不一样的请求方式进行全局管理,由于咱们整个项目都使用的post请求,我把全局的请求须要的reqfrom和appkey都写在了此处,这里不具体去写get方法如何封装,和post基本上差很少。可能会有一些开发者忽略的问题,就是当请求为form格式时,须要使用qs包的stringify方法进行参数的格式化,若格式为json时则不须要。咱们的接口恰好都有这两种格式,因而我也作了区分。由于接口都是post请求,将其判断写在post中。token的失效判断我也根据接口的实际,写在了此处,若失效便跳转登陆页json
export function post(url,data, type){ // 设置公共请求参数 userid:用户id reqfrom:请求来源 appkey:机构代码(惟一) if(localStorage.jsonObject) { let userObj = JSON.parse(localStorage.jsonObject); data.userid = userObj.userid ? userObj.userid : ''; } data.reqfrom = vm.$api.reqfrom; data.appkey = vm.$api.appKey; if(type==='json'){ axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; data.mobilePhone = localStorage.mobilePhone } else { axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; data = qs.stringify(data); } return new Promise((resolve,reject) => { axios.post(url,data) .then(response => { let code = response.data.code; if(type !=='json'){ if(response.data.msg == 'token已失效' || code == '99999'||code=='99997'||code=='40001'||code=='99996'||code=='"99998'||code=='9999'){ localStorage.removeItem('token'); localStorage.removeItem('jsonObject'); router.replace({ path: 'login', query: {redirect: router.currentRoute.fullPath} }) } } resolve(response.data); },err => { if(err.message.indexOf('timeout') !== -1) { vm.$toast.bottom('请求超时') } reject(err); }) }) }
接下来在main.js中,挂载封装的方法到vue的实例上,即可以在每一个组件进行调用。axios
import { post, get } from "./http"; Vue.prototype.$post = post; Vue.prototype.$get = get;
页面是否须要登陆才能访问,这个利用了路由的元信息结合路由钩子函数beforeEach来判断,以下。
router.js
main.js
这里的判断逻辑也很是简单,若是须要登陆权限便去取locaStorage里面的token,没有便放行。若是须要登陆权限但本地不存在token的状况下跳转登陆页,若是token失效如何判断,已经写在上述的http.js中,如果接口返回失效,也会去跳转登陆页,至此,咱们的登陆配置就完成了。固然页面可能远远不止这么简单的逻辑判断,会有更复杂的判断须要去作,就须要根据我的项目的实际状况去增长了。api
router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { // 判断该路由是否须要登陆权限 let jsonObject = JSON.parse(localStorage.getItem('jsonObject')); // 容错,若localStorage不存在对象,初始化一个空对象 if (jsonObject == null) { jsonObject = {}; } else { next({ path: '/login', // query: { redirect: from.fullPath } query: { redirect: router.currentRoute.fullPath } }) } } else { next(); } });