前言:
做的第一个支付宝小程序,支付宝会员日抢购的一个卡券类项目。考虑到流量会比较大,授权登陆放到用户第一次能直接访问的需要登陆的页面(或页面某个操作)进行处理。访问需要登陆的接口请求返回登陆失效的结果之后进行重新登陆,登陆成功后需要重新回到当前页面。
需求分析:
1. 登陆逻辑的处理:
用户首次访问的入口页面需要登陆的不止一个,所以登陆逻辑最好是进行统一风封装复用;
2. 登陆失效的处理:
这个支付宝小程序项目并没有登陆页,且小程序外部入口较多,所以登陆失效跳回到入口页面不仅体验不好,而且实现起来也比较复杂。
也考虑过接口登陆失效后调用登陆模块,登陆成功后回调之前的请求 A(params)=>{B(A(params)} ,但我们的接口请求是经过统一封装的,登陆失效的处理逻辑也是在封装里边的,那么回调也是在封装里边进行的,并不能同步到页面的数据进行重新赋值,也就无法重新渲染。当然,你也可以直接将登陆逻辑放到页面中去,那就是所有需要登陆的接口的处理都要放到页面中去了,那就比较麻烦了。
最后想到的最佳的解决方案就是登陆失效后重新刷新当前页面,虽说比不上重新登陆回调之前请求的体验好,但是实现上就会容易的多了,而且交互上做好登陆相关的提示,体验也还是挺不错的了。
需求实现:
1. 登陆封装:
鉴于项目中已经封装了网络请求,且登陆的相关逻辑需要引入网络请求的相关封装模块,也进行了一番探索,最终还是把登陆的逻辑封装在app.js中:
//app.js import http from "./api/http" App({ ...... /** * 2. 自动登录业务逻辑 */ login: function() { let self = this; my.getAuthCode({ //授权类型,默认 auth_base。支持 auth_base(静默授权)/ auth_user(主动授权) / auth_zhima(芝麻信用) scopes: ['auth_user'], success: res => { let authCode = res.authCode console.log("authCode:", authCode) if (authCode) { // 访问用户登录接口获取usertoken http.userLogin(authCode).then(data => { if (!my.isEmpty(data.userToken)) { my.setStorageSync0("usertoken", data.userToken) if (my.getStorageSync0("currentPageUrl")) {
my.reLaunch({ url: my.getStorageSync0("currentPageUrl") });
} } else { console.log("userLoginError:", JSON.stringify(data)) } }) } }, fail: res => { console.warn("getAuthCode:", res) my.confirm({ title: '温馨提示', content: '登录授权失败,您可以尝试重新授权', confirmButtonText: '重新授权', cancelButtonText: '取消', success: (result) => { if (result.confirm) { self.login() } else { //取消登陆,需要返回上一页 if (my.getStorageSync0("currentPageUrl") == "/pages/my/my") { //我的页面(tab页面需要使用relanch跳转到首页) my.reLaunch({ url: '/pages/index/index' }) } else { //针对其他页面,返回上一页 my.navigateBack({ delta: 1 }) } } }, }); } }) } ...... });
说明:
- 代码中的 my.isEmpty(value) getStorageSync0(key) my.setStorageSync0(key,value) 等方法均为针对支付宝小程序的特性自己封装的公共方法;
- 页面初始化接口登陆失效——这种情况可以采用静默登陆,不提示(用户看到小程序原生的授权登陆就能明白怎么回事),登陆成功之后重新加载当前页面进行初始化即可;
- 用户主动触发接口请求登陆失效——如用户单击事件调用接口,重新登陆打断了用户的操作,如果还想上边一样静默登陆不提示,那么用户会有点懵的。然而如果在重新授权登陆的过程中给出相关提示,那么用户重新执行之前的操作就好了,这样体验就好的多了。
- 关于在封装方法中重载当前页面——支付宝小程序并没有提供直接获取页面路径及参数的API,所以这个就只能在需要重载的页面保存页面的路径+参数的完整path了,下边会详细说明。
- 如果用户取消授权登陆——那么就只有让用户返回上一页了,其中Tab页需要重载首页。
2. 页面登陆及页面路径保存:
//main.js 公共方法封装 ...... //将当前页面路径及参数保存到缓存中(登陆失效自动登陆后relaunch()) my.getCurrentPageUrlWithArgs=function(options) { const pages = getCurrentPages() const currentPage = pages[pages.length - 1] const url = currentPage.route let urlWithArgs = `/${url}?` for (let key in options) { const value = options[key] urlWithArgs += `${key}=${value}&` } urlWithArgs = urlWithArgs.substring(0, urlWithArgs.length - 1) my.setStorage0("currentPageUrl",urlWithArgs) }
//page.js
import http from "../../api/http" var app = getApp(); Page({ ...... onLoad(e) { my.getCurrentPageUrlWithArgs(e) this.autologin() }, autologin() { //未登陆首次访问 if (my.isEmpty(my.getStorageSync0("usertoken"))) { app.login() } else { this.getUserInfo() } }, ...... })
说明:
- 页面onLoad的时候,调用 my.getCurrentPageUrlWithArgs() 方法保存当前页面的完整路径;
- 关于登陆,如果页面作为首次登陆的入口,如果登陆过则直接初始化,否则调用登陆方法。
3. 网络请求封装:
接口请求新增了 clickRequest 参数,有此参数,则给出相关提示,否则静默登陆不予提示:
...... const http = (params) => { return new Promise((resolve, reject) => { my.request({ ...... success: function(res) { //my.hideLoading() if (res.status == 200) { //需要登录、后端返回登录失效代码,需要自动登录然后重新加载小程序 if (!params.noNeedLogin && res.data.s == "302") { my.removeStorageSync({ key: "usertoken" }) //根据接口的调用是否是用户主动调用来确定是否给出提示 if (params.clickRequest) { my.toast("登陆失效,重新登陆中...", function() { getApp().login(() => { my.toast("登陆成功") }) }) } else { getApp().login() } return; } ...... } else { errorToast(); console.error(res) } }, fail: function(e) { errorToast(); reject(e) } }) }) } ......
后记:
每新做一个项目,都会尽可能的对现有框架进行提升优化,这样不仅对当前项目的开发有帮助,也有利于以后其他类似项目的复用。力求精简代码,提升效率!有感兴趣的小伙伴可以多多留言讨论,共同探索前端技术。