前情
最近新接了一個全新項目,是類似商城的小程序項目,我負責從0開始搭建小程序,我選用的技術棧是uni-app技術棧,其中就有一個用戶登錄功能,小程序部分頁面是需要登錄才可以查看的,對于未登錄的用戶需要引導用戶去登錄頁面,再back回來重新渲染當前頁面,以讓用戶正常使用
思考
問題也不復雜,就是判斷登錄狀態而已,需要登錄的頁面沒登錄就引導去登錄再回來,回來后再重新渲染頁面數據即可
這里有二個動作:一個是判斷登錄態,一個是重新渲染頁面數據,有動作就有觸發時機,對于判斷登錄態,我們是在跳轉前判斷,還是在跳轉進需要登錄的頁面再判斷,對于重新渲染數據當然是進入頁面的時候再重新渲染,但是怎么去實現重新渲染了,對于小程序我們第一時間想到的是通過生命鉤子來做,那當然就是onShow了
解決方案
基于上述的思考,我想到如下二種解決方案:
應該能解決的方案應該有很多,這只是我在實現這個需求的時候想到的二種方案
場景方案1:跳轉進需要登錄的頁面再判斷是否是登錄態,同時登錄回來后通過onShow生命鉤子重新渲染頁面
此方案優點:就是判斷登錄態你不需要特定代碼去判斷,在服務端接口這一塊做下處理即可,如果返回狀態碼是401或者是你和服務端溝通好的錯誤碼時再引導去登錄頁,這樣全局做請求攔截就行,項目中我就是有做這一塊的處理,使用的是我封裝好的工具方法:常用工具方法 - DCloud 插件市場已分享到插件市場,歡迎使用
此方案的缺點:在未登錄的狀態下,用戶會看到明顯的頁面跳轉,跳轉到一個空白頁面,突然又跳轉到登錄頁,用戶體驗不是特別好,同時在onShow生命周期鉤子里做數據重新渲染會有一個問題,這樣會造成過多的網絡請求,如果用戶量不小的話會對于服務器造成一些壓力
場景方案2:跳轉進需要登錄的頁面前判斷登錄態,并記錄正在跳轉的頁面,登錄后重定向到前面已經記錄的跳轉頁面
此方案優點:避免了onShow頻繁觸發導致服務器渲染浪費的問題,缺點就是你需要在每一個跳轉需要登錄的頁面前做登錄態判斷,會導致代碼冗余工作量增加,后期維護不是特別好
方案選擇
我選擇的是場景方案2
二種方案都有優缺點,方案1有一點是用戶感受最直接的,就是閃跳的用戶體驗那一點,至于onShow會導致接口頻繁請求問題是有方法解決的,后面會提到;方案2只要想辦法解決代碼冗余的問題即可
方案實現細節
解決代碼冗余問題,我們使用uni-app的攔截api來做下路由攔截即可,根據跳轉的URL和當前登錄態判斷要不要先跳登錄頁做登錄,在main.js中增加路由攔截,關鍵代碼如下:
.../*** 需要登錄才能跳轉的頁面*/
const needLoginPages = ['/orders/detail/detail','/orders/orderList/orderList',...
]// 要攔截的路由方法
const interceptors = ['navigateTo', 'reLaunch', 'redirectTo']
const globalStoreInstance = globalStore(pinia);// 路由攔截
interceptors.forEach(interceptor => {uni.addInterceptor(interceptor, {invoke(e) {// 判斷當前頁面是否是要需要登錄才能跳轉的頁面里const needLogin = needLoginPages.findIndex(item => e.url.includes(item)) !== -1;if (needLogin && !storage.get(TOKEN)) {// 記錄要跳轉的頁面globalStoreInstance.setNeedLoginBackPage(e.url);uni.navigateTo({url: '/other/login/login'})return false}return true}})
})...
我在寫這篇博客的時候,我發現這里代碼其實有一個可優化點,你發現了嗎?歡迎留言👀討論
同時在登錄頁登錄成功后需要做一下跳轉邏輯,關鍵代碼如下:
...
// 解決登錄后跳轉的問題
if (globalStoreInstance.needLoginBackPage) {uni.redirectTo({url: globalStoreInstance.needLoginBackPage,complete: () => {globalStoreInstance.setNeedLoginBackPage('');}})
} else {// 解決登錄回去頁面數據丟失的問題const pages = getCurrentPages();if (pages.length >= 2) {// 獲取前一個頁面實例const prevPage = pages[pages.length - 2];// 調用前一個頁面的onLoad方法if (prevPage?.onLoad) {prevPage.onLoad(prevPage.options || {}); // 傳遞原始參數}}uni.navigateBack();
}...
看代碼除了跳轉還處理了back,這一段back邏輯也是我在實現的時候發現它可以解決方案1的onShow問題,也就是說方案1也就是只有一個體驗問題,所以二種方案我覺得都是可行的,同時我提供方案1接口攔截的代碼:
import { Request, storage } from '@/uni_modules/hbxw-utils/js_sdk/hbxw-utils.js';
import { BASE_URL } from '@/config/http';
import { TOKEN } from '@/config/common';const request = new Request({isLogin: true,
});request.baseUrl = BASE_URL;/*** 請求攔截,可以通過add方法添加多個* 參數為請求配置,可以對請求參數做一些特殊處理*/
request.requestIntercept.add((requestConfig) => {// 如果有傳就用傳的,沒有就去取,為了解決登錄默認tokenconsole.log('---- requestConfig ----:', requestConfig)if (!requestConfig.header) {requestConfig.header = {}}// 如果header中沒有Accept,則設置為application/jsonif (!requestConfig.header?.Accept) {requestConfig.header.Accept = 'application/json';}if (!requestConfig.header?.Authorization) {let Authorization = ''try {Authorization = storage.get(TOKEN) || '';} catch (err) {console.log(err)}// 添加Authorization到header中用于服務端登錄判斷if (Authorization) {if (!requestConfig.header) {requestConfig.header = {}}requestConfig.header.Authorization = Authorization;}}// 如果返回true則請求會中斷// return true;
});/*** 響應攔截,可以通過add方法添加多個* 第一個參數為請求響應體* 第二個參數為請求配置信息*/
request.responIntercept.add((response, requestConfig) => {console.log('---- response ----:', response)// 如果接狀態碼為401,而且當前接口是需要判斷登錄狀態的if (response.statusCode == 401 && requestConfig.isLogin) {uni.navigateTo({url: '/other/login/login'})// 返回true 中斷后面處理return true;}// 通用錯誤處理if (response.statusCode !== 200 || response.data.code !== 200) {uni.showToast({title: response.data.message || '請求失敗,請稍后再試',icon: 'none'})return true;}
});export default request;
期望
解決問題的方法千千萬,上述是我是解決登錄跳轉邏輯的處理方案,如果在上面二種方案中,你會選擇哪一種了?聰明的你也一定有別的更好的方案,期待你的分享和留言👀,共同進步。