登入,幾乎什麼項目都會用到,其重要性不言而喻,而小程式的登入卻一直是為人頭疼的一件事,這裡我分享下我們在小程式登入上的探索。
通常的登入都是通過一個表單,這很正常,但如果在小程式裡你也這麼做那就有點不可思議了,微信的一鍵登入對使用者體驗有多好你難道不知道?不用是不是腦子有坑?最主要你要利用微信的生态必須需要用微信的登入,以擷取相關資訊來和微信互動,OK,我們進入正題。
觸發登入授權彈窗
使用者在小程式、小遊戲中需要點選元件後,才可以觸發登入授權彈窗、授權自己的昵稱頭像等資料。
友情提示一下:
wx.login
并不需要點選元件,需要的是
wx.getUserInfo
,但通常我們都會用到
UnionID
、
encryptedData
、
iv
等資訊完成完整的登入流程,本文主要聚焦的也是這種場景。
是以之前直接通過調用API的方式就行不通了,那麼問題來了——這個點選按鈕要放到哪裡?
放到首頁,一進小程式就必須先登入。這樣顯然很粗暴,而且問題并沒有解決,反而會把使用者直接拒之門外,畢竟你不是用小程式做背景系統,什麼場景都需要授權,先授權也是必須的。
在需要授權的時候跳到登陸頁面。這樣就解決了上面遇到的不需要授權的時候也被強制授權,可是這樣好嗎?
體驗上不好,操作被打斷,尤其整個頁面都不需要授權隻有在一個地方需要授權的,例如:你正在讀一篇文章,讀罷深有感觸,想評論一番,洋洋灑灑幾十字寫完正準備點選呢,他媽的跳轉了!跳轉了!
又一個漏鬥,增加使用者流失率。還TM要登入!很多使用者心裡一定這麼想。
那就直接放在需要登入的頁面上(這不是漏鬥嗎?很多讀者一定這麼想。但想想看上面那個場景,點評論時隻是需要點選下彈出的登入按鈕,而且還假模假樣的以微信的口吻提醒你需要登入,那你會不會登入?最起碼你很願意登入,而且來的很突然,我控幾不住自己的手就點了!點了!)。
可是這種方式有一個問題:怎麼在需要的頁面都能彈出登入按鈕?
彈出登入按鈕
應該很多人都能想到:抽離出元件,那怎麼保證在需要的頁面都有這個元件呢?錯殺一千也不能放過一個!把登入元件內建到共用的父元件,然後在每個頁面都使用。我也建議這麼做,因為這個共用的父元件其實又很多用處,例如iPhoneX适配等。
等等,什麼都準備好了,什麼時候需要登入呢?XX,這個肯定是你自己控制的啦。嗯~好吧,我們來理一理。
校驗是否需要鑒權
請求接口的時候,嗯~這是大家的共識。BOSS來了——怎麼鑒權?

官方的這張圖已經做了很詳盡的說明,這裡不做贅述。
但是看到
session_key
了嗎?看到官方同時說的了嗎?
是以問題又來了:怎麼保證
session_key
的有效性?
session_key 的有效性
誠如上圖:
- 要保證調用接口時後端
不失效,隻能在每次調用前先使用session_key
檢查是否有效wx.checkSession
- 實踐中也發現
非常耗時,大約200ms,是以也不能每次接口調用前都使用wx.checkSeesion
檢查是否有效wx.checkSession
- 同時要注意⚠️前端不能随便重新執行
,因為可能導緻正在進行的其它後端任務wx.login
失效session_key
天啦噜,怎麼辦?!通過實踐和偶然的發現——官方的示例代碼:
得知:在使用小程式期間session_key是不會失效的,so,你想到了什麼?
- 在每個請求前去校驗有效性
- 将校驗有效性的結果存儲起來
- 通過async/await和剛才存儲起來的結果來保證不過多調用
wx.checkSession
先問個問題:你準備用什麼方式來存儲校驗的結果?
讓思考先飛一會。
storage嗎?當然可以,不過不夠完美,為什麼?因為storage是永久的存儲,而
session_key
的有效期卻隻是在使用小程式期間,是以你需要在小程式結束後手動重置該狀态以重新校驗其有效性,那是不是在app的onUnload裡重置呢?不是!開發過小程式的應該都知道,那就是結束使用小程式的方式太多,不能保證每種方式都會觸發onUnload,例如使用者直接銷毀了微信程序(其實你也可以在app的onShow裡搞)那用什麼呢?直接用記憶體啊,借助記憶體的自動管理來智能管理,是以最終代碼應該是這樣的:
// doRequest.js
let wxSessionValid = null // 微信session_key的有效性
// 帶鑒權的請求封裝
async function doRequestWithCheckAuth() {
...
if (typeof wxSessionValid !== 'boolean') {
wxSessionValid = await checkWxSession() // 檢查微信session是否有效
}
if (!wxSessionValid) {
await reLogin() // 重新登入
}
wxSessionValid = true // 重新登陸後session_key一定有效
...
}
這樣是不是看起來比較完美了?嗯~
不知道有沒有同學着急問業務側的session(自定義的登入态)怎麼沒講?嗯,那現在講吧。
校驗認證體系
怎麼校驗完整的認證體系?其實很簡單,都不想把它作為一部分來講,但既然講了就必然有我想強調的:校驗微信端的
session_key
略有麻煩,但不應該把它抛給服務端。
- 服務端不能直接校驗
的有效性而是通過調用接口發現錯誤了才知道失效了,這是被動的session_key
- 服務端需要同時維護兩個session
而放在前端我們隻需要校驗兩個session的有效性即可,任何一個失效就重新登入,這是積極主動有效的操作,應該被提倡。
貫通
OK,基本上梳理的差不多了,就差彈登入按鈕了,這個簡單,調用剛才封裝的元件的方法就行了嘛,bingo,可是,點完允許後呢?怎麼繼續使用者的操作呢?怎麼能讓使用者的體驗不被打斷呢?先回放下剛才reLogin的代碼:
async function reLogin() {
// 確定有使用者資訊
await new Promise(resolve => { // ⚠️注意開頭有await!!!
wx.getSetting({
success: (res) => {
// 如果使用者沒有授權或者沒有必要的使用者資訊
if (!res.authSetting['scope.userInfo'] || !_.isRealTrue(wx.getStorageSync('userInfoRes').userInfo)) {
navToLogin(resolve) // 去提示使用者點選登入按鈕,⚠️注意:并把目前的resolve帶過去
} else {
resolve() // 靜默登入
}
}
})
})
return new Promise((resolve) => {
wx.login({
success: res => {
login(res.code).then((jwt) => {
resolve(jwt) // resolve jwt
}) // 通過code進行登入
},
fail(err) {
wx.showToast({
title: err.errMsg,
icon: 'none',
duration: 2000
})
}
})
})
}
function navToLogin(resolve) {
/* eslint-disable no-undef */
const pages = getCurrentPages()
const page = pages[pages.length - 1] // 目前page
page.openLoginModal(resolve) // 打開登入按鈕彈框,并把目前的resolve帶過去
}
上面的代碼注釋裡有兩個⚠️注意看到沒?是的,通過回調的方式當使用者同意授權了就繼續餘下的邏輯,如果被拒絕了,則安利他,再拒絕就終止操作,下次需要授權也會繼續彈出授權。
有不明白歡迎評論留言指出,我再做說明修改,謝謝。