何为微信登录
官网文档如是说:小程序能够通过微信官网提供的登录能力不便地获取微信提供的用户身份标识,疾速建设小程序内的用户体系。换句话说就是让用户在咱们小程序的用户 id 和用户的微信 open_id 相关联。这样用户拜访时,咱们能够通过获取用户的 open_id,从而晓得用户在咱们平台的 id。这样用户就能够不必输出账号密码去登录本人的账号了。
登录流程
整个登录流程官网文档中也给了一个时序图,如下:
须要解释的是,开发者服务器就是咱们的后端服务。所以要在微信小程序中实现用户微信快捷登录,须要前后端的配合实现(全栈开发除外)。前端侧次要实现的过程是图中的 1,2,6,7 四个步骤。
具体实现
有了上边的时序图,咱们实现登录逻辑其实就比较简单了,照着流程做就是了。第一步当然是要领有一个微信小程序我的项目,笔者应用了 uni-app 初始化的我的项目,还没有生成我的项目的小伙伴能够移步我的另一篇博客:
uni-app 开发小程序系列--我的项目搭建
何时触发用户登录?
何时触发用户登录?这个中央不同的利用有不同的要求和设计。在笔者看来,因为登录过程其实是静默的,用户无感知的,也未波及获取用户的隐衷数据,所以在用户间接拜访时就立马登录生成用户会话态是最好的。这样咱们也好剖析访客的数据。在 uni-app 我的项目中,有个根组件 App.vue,咱们能够抉择在这里 onLaunch 生命周期函数中去做登录逻辑解决。笔者代码如下:
<script>export default { onLaunch: function() { // 从storage获取登录信息,没有则须要登录 let tokenInfo = uni.getStorageSync("tokenInfo"); let hasValidToken = false; if (tokenInfo) { let time = new Date().valueOf(); // 存储工夫小于token生效工夫,才是无效token, 否则从新受权 hasValidToken = time - tokenInfo.timestamp < 3600 * 24 * 1000; } if (!hasValidToken) { // 调用小程序登录api uni.login({ provider: "weixin", success: (wxInfo) => { // 获取到code后,提交给服务端 this.$api.post('/wxa/login', { code: wxInfo.code, }).then((res) => { // 存储获取到的token uni.setStorage({ key: 'tokenInfo', data: { token: res.token, timestamp: new Date().valueOf() } }) }) }, }); } }, onShow: function() { console.log('App Show') }, onHide: function() { console.log('App Hide') }}</script>
上边代码曾经蕴含了时序图中的 1,2,6 步骤。有几个中央须要解释一下:
- 为何应用 storage?
storage 是官网提供的 api, 用于将数据存储在本地缓存中指定的 key 中。除非用户被动删除或因存储空间起因被零碎清理,否则数据都始终可用。单个 key 容许存储的最大数据长度为 1MB,所有数据存储下限为 10MB。所以咱们如果把 token 存储到 storage 里后,能够缩小对 wx.login 接口以及后端接口的频繁调用。
- 为何要判断 token 是否生效?
笔者的思路是存储 token 时顺便记录下工夫戳。获取 token 时,会拿以后工夫戳减去存储时的工夫戳失去存储工夫,而后判断是否超过 30 天(当然这个工夫能够和后端协商)。如果超过,则为生效,那么则须要从新去登录。有的人感觉没必要做这一步,如果 token 生效,会在调用其余接口时失去一个相似 401 之类的错误码,而后再去从新登录就行。当然这也是能够的,只是笔者感觉在这里解决的话,逻辑更加集中,且体验绝对好一些。
如何携带登录态?
当咱们本地获取了 token,则示意登录胜利,接下来是在其余申请中携带上 token,这样后端才晓得是哪个用户的申请。咱们当然不能在每个申请中去加这个逻辑,这样工程量很大。所以,最好先对 wx.request 进行一次封装,而后所有的申请走咱们封装后的办法。笔者代码如下:
// 该处配置为后端接口地址const defaultHost = "http://api-server.com";const errorMsg = (response) => { let error = {}; if (response.statusCode) { error.code = response.statusCode; switch (response.statusCode) { case 400: error.msg = "谬误申请"; break; case 401: error.msg = "未受权,请从新登录"; break; case 403: error.msg = "回绝拜访"; break; case 404: error.msg = "申请谬误,未找到该资源"; break; case 405: error.msg = "申请办法未容许"; break; case 408: error.msg = "申请超时"; break; case 500: error.msg = "服务器端出错"; break; case 501: error.msg = "网络未实现"; break; case 502: error.msg = "网络谬误"; break; case 503: error.msg = "服务不可用"; break; case 504: error.msg = "网络超时"; break; case 505: error.msg = "http版本不反对该申请"; break; default: error.msg = `连贯谬误${response.statusCode}`; } } else { error.code = 10010; error.msg = "连贯到服务器失败"; } return error;};function request(path, method, data, setting) { const tokenInfo = uni.getStorageSync("tokenInfo"); const host = setting ? setting.host || defaultHost : defaultHost; const token = setting ? setting.token || tokenInfo.token : tokenInfo.token; return new Promise((resolve, reject) => { uni.request({ url: host + path, method: method, data: data, header: { Authorization: "Bearer " + token, }, success: (res) => { // 状态码非200的解决 if (res.statusCode >= 400) { const error = errorMsg(res); uni.showToast({ title: error.msg, icon: "none", }); reject(errorMsg(res)); // errorCallback(errorMsg(res)) } else if (res.data.code) { uni.showToast({ title: res.data.msg, icon: "none", }); // reject(errorMsg(res.data.msg)) // errorCallback(res.data) } else { resolve(res.data); // successCallback(res.data) } }, }); });}export default { get: (path, data, otherData) => { return request(path, "get", data, otherData); }, post: (path, data, otherData) => { return request(path, "post", data, otherData); }, request: request,};
记住上述代码也有几个中央须要留神:
- defaultHost 是后端接口的域名局部,如果是有分测试环境实在环境,那么该处最好依据环境判断一下;
- 笔者是依照 jwt 标准在 header 中增加的 token 信息,这里的规定也是能够和后端进行协商;
- 对于错误码的判断,笔者判断如果后端返回了自定义的错误码时,返回 0 为失常响应,不为 0 则都算异样。这里须要依据理论状况进行调整。
封装了申请后,咱们就能够间接调用须要登录态能力拜访的接口了。比方获取用户信息:
<template> <div class="my"> <div>用户ID:{{userInfo.id}}</div> <div>用户昵称:{{userInfo.username}}</div> <div>用户openId:{{userInfo.openId}}</div> </div></template><script setup> import { ref } from "vue"; import request from "@/common/request.js"; const userInfo = ref({}); request.get("/user/info").then((res) => { userInfo.value = res; });</script><style lang="scss" scoped> .my { width: 100%; }</style>
至此,登录性能实现。
后记
本次工作只是实现了用户登录,如果想获取用户昵称头像甚至手机号,那还须要申请用户受权,比较复杂,笔者将在下篇博客中具体介绍。
对于作者
计算机专业科班出身,8 年+Web 开发教训,多年深耕 Vue2,Vue3 技术栈。全栈开发经验丰富,技能树笼罩从前端工程搭建到部署上线全链路流程。紧跟技术潮流,始终关注着各种新兴技术趋势并踊跃进行实际摸索,谋求优雅的开发体验,极致的开发效率,高标准的开发品质。
欢送批评指正,或者与我交换探讨前端技术。源码尚未公开,分割我可私发。
分割我:imwty2023(微信)
博客原创地址:uni-app开发小程序系列--微信登录 | imwty