接上篇《【Geek議題】合理的VueSPA架構討論(上)》傳送門。javascript
登陸狀態標識符跟token相似,都是須要自動維護有效期,但也有些許不一樣,獲取過程只在用戶登陸或註冊的時候,不須要自動獲取。
本人比較推薦使用公共狀態管理vuex進行自動化管理,並配合路由鉤子,減小代碼編寫時的顧慮。vue
示例中公共狀態管理中的user模塊裏定義了userIdObj,其中包含了userId登陸狀態標識符和過時時間。
維護userId是否過時主要是經過vuex中的getter來實現。java
const getters = { getUserId: (_state) => { // 獲取公共狀態中的userIdObj const userIdObj = { ..._state.userIdObj, }; // 是否過時標識 let isExpire = false; // 判斷是否過時 if (userIdObj && userIdObj.userId) { isExpire = new Date().getTime() - userIdObj.expireTime > -10000; } // 若是過時則返回空字符串(不必定) if (!userIdObj || !userIdObj.userId || isExpire) { return ''; } // 沒過時則返回userId return userIdObj.userId; }, };
回顧上篇中全局的路由鉤子router.beforeEach
,當目標路由元信息requiresAuth爲true則表示,這個路由必須有登陸狀態才能訪問,這時候就會進行登陸狀態檢查。處理思路以下:ios
【PS】示例這裏的處理還不完美,最好跳轉登陸前保存好目標路由,登陸成功就直接跳轉去該路由。
router.beforeEach((to, from, next) => { // ... // 檢查登陸狀態 if (to.meta.requiresAuth) { console.log('目標路由須要登陸狀態'); if (!store.getters.getUserId) { console.log('內存無登陸信息,嘗試在本地存儲中找'); const localUserIdObj = JSON.parse(localStorage.getItem('userIdObj')); if (localUserIdObj) { // 若是本地存儲中有userIdObj,則提交到公共狀態 store.commit('setUserIdObj', localUserIdObj); } } // 再次檢查公共狀態裏有沒有userId if (!store.getters.getUserId) { console.log('依舊無登陸信息'); router.push({ name: 'userLogin', }); } } next(); });
在「頁面」中獲取userId也很簡單,使用計算屬性是最好的,返回的userId具備響應性,這作的好處也是爲了實時將登陸狀態反應到頁面上,纔不會出現顯示已登陸,但用戶刷新一下才能知道登陸狀態已過時的尷尬狀況。vuex
【PS】一些須要用戶登陸狀態的api函數,也是經過這樣的方法獲取並使用。固然,建議在非首屏加載使用的api函數(好比提交表單),須要在調用前,檢查一下userId還存不存在,以避免出錯。
computed: { // 獲取登陸狀態 userId() { return this.$store.getters.getUserId; }, },
有時會有須要取消請求的需求,好比上傳文件耗時過長,用戶不想等,這時候就必須有取消的功能。
上篇提到的axios已經提供了一個基於CancelToken的取消機制,這裏咱們要配合vuex實現對全局鏈接的實時監控。axios
【PS】示例的接口名稱都是在寫api函數的時候定義好的,想要更加靈活,能夠每次請求都單獨指定名字。
示例中給公共狀態下的com模塊添加了cancelToken數組,用來保存發起的鏈接的名稱和source(取消標記),並提供了以下三個mutations方法segmentfault
// 增長cancelToken addCancelToken(_state, cancelToken) { _state.cancelToken.push(cancelToken); const arr = _state.cancelToken; _state.cancelToken = arr; }, // 刪除指定名字的cancelToken deleteCancelToken(_state, name) { _state.cancelToken.some((i, index) => { if (i.name === name) { _state.cancelToken.splice(index, 1); return true; } return false; }); }, // 清空cancelToken clearCancelToken(_state) { _state.cancelToken.forEach((i) => { if (i.source && typeof i.source.cancel === 'function') { i.source.cancel(`cancel${name}`); } }); _state.cancelToken = []; },
基本思路:api
// 請求發起前攔截器 myAxios.interceptors.request.use((_config) => { const config = _config; const source = axios.CancelToken.source(); // 獲取cancelToken config.cancelToken = source.token; // 取消請求標記保存 store.commit('addCancelToken', { name: config.name, source, }); return config; }, () => { // 異常處理 console.error('請求發起前攔截器異常'); }); // 響應攔截器 myAxios.interceptors.response.use((response) => { // 刪除取消標記 store.commit('deleteCancelToken', response.config.name); console.log(response); return response; }, (error) => { console.error(error); // 清理取消標記 store.commit('clearCancelToken'); return Promise.reject(error); });
使用計算屬性獲取cancelToken數組,這裏是響應式的,全部咱們其實能夠知道如今有多少個請求還未完成了。數組
【PS】實時對全局請求的監控已經實現,其實能夠作的擴展就不少了,好比能夠全局化loading的顯示,只要數組內一直不爲空持續一段時間就能夠判斷須要顯示loading,若是爲空則關閉loading。
computed: { // 獲取鏈接列表 cancelToken() { return this.$store.state.com.cancelToken; }, },
遍歷這個數組,查找到對應名字的鏈接就能夠取消了。架構
this.cancelToken.forEach((i) => { console.log(i); if (i.name === '上傳文件' && typeof i.source.cancel === 'function') { console.log('取消上傳'); i.source.cancel(`cancel${name}`); } });
在初始化Vue的根組件前,給Vue的原型鏈上添加經常使用的工具,能夠方便在vue文件中使用。這樣作會影響全部Vue示例推薦只在單頁面應用中使用。
好比下面以咱們的api集爲例,這樣在vue文件中this.$api
就可使用咱們的api集,不須要重複引用。
// 將api模塊掛載進vue方便在this調用 Vue.prototype.$api = api;
在util文件夾下能夠新建一個專門用來存過濾器的filter.js,而後批量導入的全局過濾器中。
import * as filters from './util/filter'; Object.keys(filters).forEach(k => Vue.filter(k, filters[k]));