上一篇:开发微信小程序必须要知道的事
登录,几乎什么项目都会用到,其重要性不言而喻,而小程序的登录却一直是为人头疼的一件事,这里我分享下我们在小程序登录上的探索
通常的登录都是通过一个表单,这很正常,但如果在小程序里你也这么做那就有点不可思议了,微信的一键登录对用户体验有多好你难道不知道?不用是不是脑子有坑?最主要——你要利用微信的生态必须需要用微信的登录,以获取相关信息来和微信交互,OK,我们进入正题
用户在小程序、小游戏中需要点击组件后,才可以触发登录授权弹窗、授权自己的昵称头像等数据
友情提示一下:wx.login 并不需要点击组件,需要的是 wx.getUserInfo,但通常我们都会用到 UnionID、encryptedData、iv 等信息完成完整的登录流程,本文主要聚焦的也是这种场景
所以之前直接通过调用 API 的方式就行不通了,那么问题来了——这个点击按钮要放到哪里?
放到首页,一进小程序就必须先登录。这样显然很粗暴,而且问题并没有解决,反而会把用户直接拒之门外,毕竟你不是用小程序做后台系统,什么场景都需要授权,先授权也是必须的
在需要授权的时候跳到登陆页面。这样就解决了上面遇到的不需要授权的时候也被强制授权,可是这样好吗?
体验上不好,操作被打断,尤其整个页面都不需要授权只有在一个地方需要授权的,例如:你正在读一篇文章,读罢深有感触,想评论一番,洋洋洒洒几十字写完正准备点击呢,他妈的跳转了!跳转了!
又一个漏斗,增加用户流失率。还 TM 要登录!很多用户心里一定这么想
那就直接放在需要登录的页面上(这不是漏斗吗?很多读者一定这么想。但想想看上面那个场景,点评论时只是需要点击下弹出的登录按钮,而且还假模假样的以微信的口吻提醒你需要登录,那你会不会登录?最起码你很愿意登录,而且来的很突然,我控几不住自己的手就点了!点了!)
可是这种方式有一个问题
怎么在需要的页面都能弹出登录按钮
应该很多人都能想到:抽离出组件,那怎么保证在需要的页面都有这个组件呢?错杀一千也不能放过一个!把登录组件集成到共用的父组件,然后在每个页面都使用。我也建议这么做,因为这个共用的父组件其实又很多用处,例如 iPhoneX 适配等
等等,什么都准备好了,什么时候需要登录呢?XX,这个肯定是你自己控制的啦。嗯~好吧,我们来理一理
在哪里校验是否需要鉴权
请求接口的时候,嗯~这是大家的共识
BOSS 来了
怎么鉴权
官方的这张图已经做了很详尽的说明,这里不做赘述 但是看到 session_key 了吗?看到官方同时说的了吗
所以问题又来了
怎么保证 session_key 的有效性
诚如上图
要保证调用接口时后端 session_key 不失效,只能在每次调用前先使用 wx.checkSession 检查是否有效
实践中也发现 wx.checkSeesion 非常耗时,大约 200ms,所以也不能每次接口调用前都使用 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 带过去
}
上面的代码注释里有两个⚠️注意看到没?是的,通过回调的方式???? 当用户同意授权了就继续余下的逻辑,如果被拒绝了,则安利他,再拒绝就终止操作,下次需要授权也会继续弹出授权
有不明白欢迎评论留言指出,我再做说明修改完整源码以后会放出的,通过 wepy 搭建的一个框架 下一篇会讲 api 的封装谢谢