以前在一家面試時,面試官問及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(); } });