【Geek議題】合理的VueSPA架構討論(下)

接上篇《【Geek議題】合理的VueSPA架構討論(上)》傳送門javascript

自動化維護登陸狀態

登陸狀態標識符跟token相似,都是須要自動維護有效期,但也有些許不一樣,獲取過程只在用戶登陸或註冊的時候,不須要自動獲取。
本人比較推薦使用公共狀態管理vuex進行自動化管理,並配合路由鉤子,減小代碼編寫時的顧慮。vue

妙用公共狀態管理維護userId

示例中公共狀態管理中的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;
  },
};

路由鉤子中處理userId

回顧上篇中全局的路由鉤子router.beforeEach,當目標路由元信息requiresAuth爲true則表示,這個路由必須有登陸狀態才能訪問,這時候就會進行登陸狀態檢查。處理思路以下:ios

  1. 使用上面定義的getter方法獲取userId;
  2. 若是能獲取到則說明有有效的userId,則時候便可跳轉到目標頁;
  3. 若是獲取到空字符串,則說明userId無效userId不存在,跳轉至登陸頁面。
【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

  1. 在「請求發起前攔截器」中獲取到source(取消標記),寫入請求配置,並提交名稱和source到公共狀態管理;
  2. 這時候經過查詢公共狀態中是否有這個名字的鏈接就能夠獲取到source進行取消;
  3. 在「響應攔截器」中咱們將已經成功的請求在公共狀態管理中移除。
// 請求發起前攔截器
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文件中使用。這樣作會影響全部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]));
相關文章
相關標籤/搜索