# 微信小程序云开发实现注册登录 与 多终端常见登录方式
TIP
从本节内容开始,正式进入到微信小程序云开发快速入门和实践应用了,对前面学到的基础知识进行综合实践。
通过前面《艾编程-coffee》项目的开发实践,完成并掌握小程序项目的开发对我们后期 Vue 的学习打好基础,同时也是我们未来学习 uni-app 多终端:iOS、Android、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等的必备前提条件。
- 当前常见的多终端登录方式
- 小程序登录
- 微信小程序云开发快速入门
- 使用微信小程序云开发实现注册登录
如下是前面已完成的微信小程序
icoding-coffee
项目(这部分内容没有图文教程,具体代码实践查看直播视频回放即可)
# 一、当前常见的多终端登录方式
TIP
深入浅出市面上常见的多终端登录方式,账号、密码登录,手机验证码登录,第三方登录,APP 扫码登录,身份证、人脸识别登录,包括本节我们会学习的微信小程序的登录等
# 1、账号、密码登录
TIP
以艾编程 PC 端官网为例,需要先注册一个账号 -> 在登录窗口输入用户名 和 密码 -> 点击登录按钮,登录成功
账号、密码登录方式的特点
- 输入麻烦
- 不便于记忆
该方式已经开始淡出市场,很多平台已经开始降低 账号、密码登录方式展示的优先级。有些终端就直接去掉了 账号、密码登录的方式。
# 2、手机验证码登录
TIP
第二种比较常见的通过手机验证码的方式登录
手机验证码登录的特点
- 麻烦度适中
- 无需记忆(自己的手机号正常不会忘记)
- 跨平台方便
- 必备的登录方式之一
# 3、第三方登录
TIP
目前很多应用不论是 PC 端 还是 移动端 都会提供第三方登录(微信、QQ、微博、支付宝等),只需通过点击对应的登录渠道来使用第三方 APP 授权当前应用即可
这种方式的好处:可以直接使用已有的账号体系来授权即可
第三方登录的特点
- 扫一扫、唤起 APP 授权登录
- 无需记忆
- 必备的登录方式之一
# 4、APP 扫码登录
TIP
APP 扫码登录仅限于同时使用 PC 端 和 移动 APP 端的用户,在手机上打开 APP 应用完成登录 -> 使用 APP 中扫一扫功能、授权 -> 完成 PC 端的快捷登录
APP 扫码登录的特点
- 扫一扫授权登录(必须保证 APP 上已经完成登录的情况下)
- 无需记忆
- 只会在 PC 端 上才会有
# 5、身份证、人脸识别登录
TIP
在金融 或 政务类的应用中会经常使用 身份证、人脸识别登录,在实际中通常会将这两者结合来使用
身份证、人脸识别登录的特点
- 金融、政务
- 无需记忆
- 主要应用在移动端上 或 一些专用的设备来实现(本质需要具备摄像头)
当然,还有指纹识别、虹膜等登录方式
# 6、登录的目的
TIP
在千行百业中,有各种各样的登录方式,不论使用哪一种方式来实现登录最终的实现目的都是一样的。
登录的目的: 在对应的系统中建立当前用户的唯一标识,即 用户 ID
注:
一个系统是可以被多个用户使用的,每个用户应该都是唯一的,并且每个用户都会在平台上产生不同的数据。我们需要区分不同用户产生的数据。
登录方式的多样化,只是社会发展 与 科技进步,使用习惯改变的必然产物,但它的目的一直没有变过。
那么,在小程序中它的登录方式又是怎样的呢 ?
# 二、小程序登录
TIP
在小程序中用户登录的方式有两种
- 借助微信小程序生态的能力,获取当前手机号 或 让用户输入手机号、再获取手机验证码的方式
- 通过调用小程序的原生 API ,wx.login(Object object) (opens new window) 来实现登录,即当前用户在此小程序内的唯一标识:openId
# 1、前后端分离架构登录流程
TIP
在前后端分离架构下,大致的登录流程都一样,以下为主流登录的实现的流程
注:
- 在客户端提供登录信息,即:在浏览器、APP 上做登录操作
- 将信息提交给服务端做判断
- 建立唯一标识,本质上就是生成一个用户 ID
- 服务端会经过一系列的逻辑运算,最终生成一个 令牌(一串加密的字符串)
- 再将该令牌返回给客户端
其中,服务端判断、建立唯一标识的过程会根据业务的复杂度 与 安全要求的不同,每一个公司的具体实现流程不一定一样。作为前端开发也并不需要这块是如何实现的,本身是由服务端开发的同学负责的。
令牌
即一段经过加密的字符串。具体表现:不能一眼从该字符串中提取出有用的信息
通过调用一个登录的接口,传递一些私有的信息,服务端就会给我们返回一个令牌,这个过程就是登录的过程
令牌的用途:
客户端在接收到令牌后,通常是需要保存令牌的,然后在所有需要身份鉴权的接口调用中附带这个令牌,服务端相关鉴权的模块就会去解密这个令牌(加密字符串)。
拿到解密后的身份信息,接着来判断当前的请求是否合法。这也是目前很常见的一种接口鉴权的流程
# 2、个人中心页面结构
TIP
- 页面结构的编写
- 登录前 和 登录后的页面结构
- 登录前后的条件控制
在 pages/me/me.wxml
中
<!--pages/me/me.wxml-->
<view class="container">
<view class="users">
<!-- 未授权登录状态 -->
<view class="userinfo" wx:if="{{!userInfo}}">
<image
src="https://www.arryblog.com/coffee/default-avatar.png"
mode="cover"
class="user-avatar"
/>
<text class="user-desc">登录获取更多会员权益</text>
<button class="btn-login" bindtap="handleLogin">授权登录</button>
</view>
<!-- 授权登录状态 -->
<view class="userinfo" wx:else>
<view class="user-login-suc">
<image src="{{userInfo.avatarUrl}}" mode="cover" class="user-avatar" />
<text class="user-login-desc">Hi {{userInfo.nickName}} ~ </text>
</view>
<button class="btn-login" bindtap="outLogin">退出登录</button>
</view>
</view>
<view class="nav-list">
<!-- 登录后显示:个人订单 和 收货地址,否则不显示 -->
<block wx:if="{{userInfo}}">
<view class="item-list">
<view class="item-wrap">
<i-icon name="dingdan1" size="33" color="#FF5A62" />
<text class="icon-text">个人订单</text>
</view>
<i-icon name="right-line" size="33" />
</view>
<view class="item-list">
<view class="item-wrap">
<i-icon name="didian_dingwei" size="33" color="#FF5A62" />
<text class="icon-text">收货地址</text>
</view>
<i-icon name="right-line" size="33" />
</view>
</block>
<view class="item-list">
<view class="item-wrap">
<i-icon name="gonggao_tongzhi" size="33" color="#FF5A62" />
<text class="icon-text">系统消息</text>
</view>
<i-icon name="right-line" size="33" />
</view>
<view class="item-list">
<view class="item-wrap">
<i-icon name="kefu" size="33" color="#FF5A62" />
<!-- <text class="icon-text">在线客服</text> -->
<button open-type="contact" class="btn-text">在线客服</button>
</view>
<i-icon name="right-line" size="33" />
</view>
<view class="item-list">
<view class="item-wrap">
<i-icon name="zhengzaijinhang" size="33" color="#FF5A62" />
<!-- <text class="icon-text">意见反馈</text> -->
<button open-type="feedback" class="btn-text">意见反馈</button>
</view>
<i-icon name="right-line" size="33" />
</view>
</view>
<view class="adv-con">
<image
src="https://www.arryblog.com/coffee/me-adv-banner-1.png"
mode="cover"
class="me-adv-img"
/>
</view>
</view>
# 3、个人中心页面样式
在 pages/me/me.wxss
中
/* pages/me/me.wxss */
.container {
padding: 28rpx;
}
.users {
width: 100%;
height: 249rpx;
background-image: url(https://www.arryblog.com/coffee/me-bgc-banner.png);
background-size: cover;
}
.userinfo {
height: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0 20rpx;
align-items: center;
}
.user-login-suc {
display: flex;
flex-direction: row;
align-items: center;
color: #fff;
}
.user-login-desc {
margin-left: 20rpx;
}
.user-desc {
color: #fff;
}
.user-avatar {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
}
.btn-login:not([size="mini"]) {
width: 150rpx;
height: 60rpx;
font-weight: 400;
font-size: 26rpx;
padding: 0;
margin: 0;
line-height: 60rpx;
background-color: #ffca8c;
color: #fff;
}
.item-list {
display: flex;
flex-direction: row;
justify-content: space-between;
line-height: 116rpx;
border-bottom: 1px solid #f6f6f6;
}
.icon-text {
margin-left: 30rpx;
}
.icon-right-line {
color: #cacad6;
}
.adv-con {
text-align: center;
margin-top: 20rpx;
}
.me-adv-img {
width: 623rpx;
height: 177rpx;
}
.item-wrap {
display: flex;
align-items: center;
}
/* 去掉按钮的默认样式 */
.btn-text:not([size="mini"]) {
width: 100%;
background-color: #fff;
border: none;
text-align: left;
margin: 0;
padding: 0;
line-height: 1.3;
font-size: 28rpx;
font-weight: 400;
margin-left: 30rpx;
}
/* 去掉边框 */
.btn-text::after {
border: none;
border-radius: 0;
}
# 4、个人中心逻辑处理
TIP
- 用户授权信息,使用 wx.getUserProfile() (opens new window) API 接口获取用户的头像昵称信息
- 将用户头像和昵称存储在 本地存储中,当用户再次进入小程序时,不必重复授权登录
- 退出登录,清空本地存储数据 同时 清空页面数据
在 pages/me/me.js
中
// pages/me/me.js
// 获取应用的实例
const app = getApp();
Page({
data: {
userInfo: {},
},
// 授权登录
async handleLogin() {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
// 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
// 获取用户信息
const res = await wx.getUserProfile({
desc: "用于完善会员资料", // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
});
// 实现用户注册(如果用户已注册,直接登录即可)
// 设置加载中的提示
// wx.showLoading({
// title: '正在授权中 ...',
// })
// 使用微信小程序云开发 或 对接我们的后端注册登录 API 接口
// 授权成功后
// 将用户信息存储在本地存储中,用于用户下次进入小程序时,不必重复授权登录
await app.setStorage("userInfo", res.userInfo);
// 更新 data
this.setData({
userInfo: res.userInfo,
});
},
// 退出登录
outLogin() {
// 清除本地缓存
app.rmStorage("userInfo");
// 更新 data
this.setData({
// 清空页面数据
userInfo: "",
});
},
// 生命周期函数 - 页面加载时执行
async onLoad() {
// 获取本地缓存中的用户登录信息
const userInfo = await app.getStorage("userInfo");
// 更新data
this.setData({
userInfo,
});
},
});
# 三、微信小程序云开发快速入门
TIP
快速了解微信小程序云开发常见基础概念,掌握登录注册必备的基础
# 1、企业项目开发到上线的环境配置
TIP
一般企业项目开发到上线的几个阶段:开发 -> 测试 -> 预发布 -> 生产,其中生产环境也就是通常说的真实线上环境。(根据企业和项目规模的不同会有不同,大致流程都一样)
# 1.1、dev(开发环境)
TIP
DEV (Development Environment)开发环境,用于软件开发人员开发联调时使用。
即:开发同学开发时使用的环境,就是与测试环境分开的独立客户机、服务器、配置管理工具等。开发同学专门用于开发及调试的服务器,配置可以比较随意, 为了开发调试方便,打开错误报告方便调试。通常表示最低
环境,由代码开发人员直接使用和维护,是代码最超前版本的一个环境。
每位开发同学在自己的 dev 分支上干活,提测前或者开发到一定程度,各位同学会合并代码,进行联调。
# 1.2、test 或 FAT(测试环境)
TIP
FAT (Feature Acceptance Test Environment)功能验收测试环境,也就是俗称的测试环境。
测试同学干活的环境,测试人员利用一些工具及数据所模拟出的、接近真实用户使用环境的环境。 目的:为了使测试结果更加真实有效。
测试环境应该与开发环境分隔开,使用独立的客户机、服务器和配置管理工具。是测试者测试及改 bug 的环境;这个环境要和生产环境类似。
开发人员确认代码分支在开发环境自测没有问题后,提交测试环境进行测试。测试环境对代码和系统已经集成,可以供测试人员进行功能模块测试,集成测试,系统测试,测试环境有独立的数据库和账号权限管理系统,由测试人员使用和管理,功能型 bug 一般在测试环境中暴露较多。
# 1.3、uat(预发布环境)
TIP
UAT (User Acceptance Test Environment)用户验收测试环境,类似于预发布环境。
预发布环境是正式发布前最后一次测试,所有的功能和配置,数据库都已经与线上环境高度相似,仅准入本次需要上线的功能代码,测试人员确认代码在测试环境经过测试用例测试没有问题后,提交预发布环境进行测试。因为在少数情况下即使预发布通过了,都不能保证正式生产环境可以 100%不出问题;
预发布环境的配置,数据库等都是跟线上一样,有些公司的预发布环境数据库是连接线上环境,有些公司预发布环境是单独的数据库;如果不设预发布环境,如果开发合并代码有问题,会直接将问题发布到线上,增加维护的成本;
预发布环境和测试环境的区别:
预生产环境和生产系统的同步性更高,几乎一样,有些测试,比如需要大数据量的,用预生产环境看程序性能比用测试环境(一般情况下数据会较少)会更准确。
注:
预发布环境一般会连接生产环境的数据库(看企业和项目规模不同),测试时要注意,以免产生脏数据,避免影响生产环境的使用
# 1.4、prod(生产环境)
TIP
PRO (Production Environment)生产环境。
即发布正式线上环境,真实用户访问的环境(最高环境,直接面向真实的用户)。由特定人员来维护,一般人没有权限去修改。该环境是指正式提供对外服务的,真实用户线上使用的环境;关闭错误报告,打开错误日志。
预发布环境和生产环境区别:
- 预发环境中新功能为最新代码,其他功能代码和生产环境一致。
- 预发环境和生产环境的访问域名不同。
# 1.5、灰度发布
TIP
灰度发布,发生在预发布环境之后,生产环境之前。
生产环境一般会部署在多台机器上(跨地域、跨机房部署),以防某地域、某机房、某台机器出现故障,这样其他机器可以继续运行,不影响用户使用。灰度发布会发布到其中的几台机器上,验证新功能是否正常。如果失败,只需回滚这几台机器即可。
# 2、创建小程序云开发环境
TIP
在微信开发中工具左上角的工具栏中,点击云开发按钮,进入 “创建云开发环境”
注:
点击确认后,需要等待一点时间,即可创建成功。
学习阶段,创建好云开发环境后不要随意删除,删除后再创建就需要付费使用了,免费额度只有一次机会。
创建成功后,会进入如下面板
# 3、常用的云开发服务
TIP
使用微信小程序云开发常用的云开发服务有:云函数、数据库、存储
# 3.1、云函数
TIP
云函数 (opens new window)是一段运行在云端的代码,无需管理服务器,在开发工具内编写、一键上传部署即可运行后端代码。
小程序内提供了专门用于云函数调用的 API。开发者可以在云函数内使用 wx-server-sdk
(opens new window) 提供的 getWXContext
(opens new window) 方法获取到每次调用的上下文(appid
、openid
等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid
)。
# 3.2、数据库
TIP
云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个 JSON 数组,数组中的每个对象就是一条记录,记录的格式是 JSON 对象。
# 3.3、存储
TIP
云开发提供了一块存储空间,提供了上传文件到云端、带权限管理的云端下载能力,开发者可以在小程序端和云函数端通过 API 使用云存储功能。
在小程序端可以分别调用 wx.cloud.uploadFile
和 wx.cloud.downloadFile
完成上传和下载云文件操作。
# 四、使用微信小程序云开发实现注册登录
TIP
深入浅出微信小程序云开发的具体实践与应用。
# 1、创建云开发环境
TIP
在项目根目录 project.config.json
项目配置文件中,初始化配置云函数的文件夹的目录
{
"cloudfunctionRoot": "cloud/"
}
在项目根目录中,新建 cloud
文件夹
选择项目对应的环境
# 2、注册用户账号
TIP
当点击登录按钮后,通过微信的开发能力获取了微信昵称和头像基础信息后,将这些基础信息或其他信息一并提交给后端,用于创建一个用户账号(注册)。
但这两个字段不能作为用户的唯一标识 !(微信昵称是会有重复的不能作为唯一标识)
注:
注册用户账号的同时,在小程序中就需要一个唯一字段 openId 来标识(伴随着微信账户本身,类似当前用户的身份证 ID 或 一样),既不会重复也不会失效。
这就是微信小程序中的鉴权机制,即 通过 openId 来鉴权。详细查阅,微信小程序官方文档 - 小程序登录 (opens new window) 流程时序图可以看到
# 2.1、新建数据库表(集合)
TIP
在云开发控制台中,创建数据表 users
用于存储用户信息
# 2.2、新建登录云函数
TIP
在 cloud
文件夹上右键 -> 选择 ”新建 Node.js 云函数“
输入云函数名称 login
即可新建成功
在 cloud/login/index.js
中编写云函数的逻辑
// 云函数入口文件
const cloud = require("wx-server-sdk");
// 1、指定云函数的环境,注:使用云函数前必须指定环境进行初始化
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); // 使用当前云环境
// 云函数入口函数
// event 指:前端传给后端的字段
exports.main = async (event) => {
const wxContext = cloud.getWXContext();
return {
event,
// 微信的唯一标识符,返回给前端
openid: wxContext.OPENID,
};
};
# 2.3、在微信端调用云函数
在 pages/me/me.js
中调用云函数
// pages/me/me.js
// 获取应用的实例
const app = getApp();
Page({
// 页面的初始数据
data: {
userInfo: {},
hasUserInfo: false,
},
// 授权登录
async handleLogin(e) {
// 1、获取用户信息
const { userInfo } = await wx
.getUserProfile({
desc: "desc",
})
.catch((err) => {
console.log(err);
});
if (userInfo) {
// 授权成功后
// 将用户信息存储在 本地存储中,用于用户下次进入小程序时,不必重复授权登录
app.setStorage("userInfo", userInfo);
// 实现用户注册(如果用户已注册,直接登录即可)
// 设置加载中的提示
wx.showLoading({
title: "正在授权中 ...",
});
// 调用云函数 -------------------
// 2、把当前用户信息提交给后端,用于创建生成一个用户账号(即:注册用户信息
wx.cloud.callFunction({
name: "login",
data: {
avatarUrl: userInfo.avatarUrl,
nickName: userInfo.nickName,
},
});
// 更新 data
this.setData({
userInfo,
hasUserInfo: true,
});
}
},
// 省略部分代码 ...
});
注:
当点击登录后,控制台会报错 “请先调用 wx.cloud.init() 完成初始化后再调用其他云 API。”
# 2.4、在小程序 app.js 中初始化云函数
// app.js
import { Storage } from "./utils/storage";
const storage = new Storage();
App({
setStorage: storage.set,
getStorage: storage.get,
rmStorage: storage.rm,
// 小程序初始化完成时触发,全局只触发一次。
onLaunch() {
this.getStorage("cartInfo")
.then((res) => {
// 设置 tarBar 图标
res.length > 0
? wx.setTabBarBadge({
index: 3,
text: String(res.length),
})
: wx.removeTabBarBadge({
index: 3,
});
})
.catch((err) => {
console.log(err);
});
// 小程序端初始化 --------------
wx.cloud.init({
// API 调用的默认环境配置
env: "icoding-coffee-dev-5dontd2874c31",
// 将用户访问记录到用户管理中,在控制台中可见
traceUser: true,
});
},
});
注:
以上 env 环境 ID 需要在云开发控制台中 复制自己的 环境 ID 即可。
此次,再次点击登录测试,后端就返回了我们需要的 openId 和 我们前端传过去的用户信息了。
# 2.5、注册用户信息 至 数据库中
TIP
当我们拿到了注册用户所需要的信息后,就开始正式注册用户信息了,即往数据库中插入一条用户数据
注:
- 如果数据库中存在当前用户的信息(已注册过),则直接返回当前用户信息(登录)
- 如果数据库中没有当前用户的信息(注册)
先完成用户信息的注册,如下
在 cloud/login/index.js
云函数中完成用户信息的注册,即往数据库中插入一条数据
// 云函数入口文件
const cloud = require("wx-server-sdk");
// 1、指定云函数的环境,注:使用云函数前必须指定环境进行初始化
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); // 使用当前云环境
// 云函数入口函数
// event 指:前端传给后端的字段
exports.main = async (event) => {
const wxContext = cloud.getWXContext();
// 解构前端传入的字段
const { avatarUrl, nickName } = event;
// 如果数据库中没有当前用户的信息(注册)
// 1、初始化数据库(获取数据库的引用)
const db = cloud.database();
// 2、指定集合(数据表),获取集合的引用
const users = db.collection("users");
// 3、在数据库中新增用户数据(注册)
const data = await users.add({
data: {
avatarUrl,
nickName,
// 注册时所需要的其他字段信息也可以再次加入 ...
// 账户余额
money: 0,
// 注册时一定要加入 openid 作为唯一标识
_openid: wxContext.OPENID,
},
});
// 添加数据库信息后,返回值(即:当前用户信息的 ID)
return {
data,
};
};
注:
编译运行后,点击登录按钮,发现 NetWork 发送请求 和 服务器返回的数据并没有发生变化。同时,数据库中也并没有插入用户信息 !
原因是:我们重新修改了云函数后,并没有上传,因此不会生效
等待上传成功后,即可正常运行,并插入数据库成功
查看云数据库中 users
集合中添加成功的数据
# 3、实现登录逻辑
TIP
当我们再次,点击登录时,就会重复注册当前用户的信息。并没有走登录逻辑,因此,我们需要来处理用户重复注册的问题。
思路: 如果数据库中存在当前用户的信息(已注册过),则直接返回当前用户信息(登录)
在 cloud/login/index.js
中编写根据 openId 查询用户信息的方法
// 云函数入口文件
const cloud = require("wx-server-sdk");
// 1、指定云函数的环境,注:使用云函数前必须指定环境进行初始化
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); // 使用当前云环境
// 云函数入口函数
// event 指:前端传给后端的字段
exports.main = async (event) => {
const wxContext = cloud.getWXContext();
// 解构前端传入的字段
const { avatarUrl, nickName } = event;
// 如果数据库中没有当前用户的信息(注册)
// 1、初始化数据库(获取数据库的引用)
const db = cloud.database();
// 2、指定集合(数据表),获取集合的引用
const users = db.collection("users");
// 在新增用户数据之前
// 3、先查询当前用户是否已注册过
const { data } = await users
.where({
_openId: wxContext.OPENID,
})
.get();
// 3、在数据库中新增用户数据(注册)
// const data = await users.add({
// data: {
// avatarUrl,
// nickName,
// // 注册时所需要的其他字段信息也可以再次加入 ...
// // 账户余额
// money: 0,
// // 注册时一定要加入 openid 作为唯一标识
// _openId: wxContext.OPENID
// }
// })
// 添加数据库信息后,返当前用户信息的 ID
return {
data,
};
};
注:
当数据库中存在该用户信息时,查询结果返回一个 数组(当前用户的信息)
如果没有查询到该用户数据时,数组即为空
# 3.1、登录注册逻辑判断
TIP
如果数据库中存在当前用户的信息(已注册过),则直接返回当前用户信息(登录),如果数据库中没有当前用户的信息(注册)
即可通过以上根据 openId 查询返回的数组的长度来进行判断,是否已存在该用户
在 cloud/login/index.js
中修改逻辑
// 云函数入口文件
const cloud = require("wx-server-sdk");
// 1、指定云函数的环境,注:使用云函数前必须指定环境进行初始化
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); // 使用当前云环境
// 云函数入口函数
// event 指:前端传给后端的字段
exports.main = async (event) => {
const wxContext = cloud.getWXContext();
// 解构前端传入的字段
const { avatarUrl, nickName } = event;
// 如果数据库中没有当前用户的信息(注册)
// 1、初始化数据库(获取数据库的引用)
const db = cloud.database();
// 2、指定集合(数据表),获取集合的引用
const users = db.collection("users");
// 在新增用户数据之前
// 3、先查询当前用户是否已注册过
const { data } = await users
.where({
_openId: wxContext.OPENID,
})
.get();
// 根据返回的 data 数组的长度来进行判断,是否已存在该用户
if (data.length === 0) {
// 等于 0 ,数据库中没有当前用户的信息(注册)
// 在数据库中新增用户数据(注册)
const res = await users.add({
data: {
avatarUrl,
nickName,
// 注册时所需要的其他字段信息也可以再次加入 ...
// 账户余额
money: 0,
// 注册时一定要加入 openid 作为唯一标识
_openId: wxContext.OPENID,
},
});
// 注册成功后,返回信息
return {
res,
};
} else {
// 如果数据库中存在当前用户的信息(已注册过),则直接返回当前用户信息(登录)
return {
data: data[0],
};
}
};
注:
更新完逻辑后,重新上传并部署云函数,再次测试
可以正常新增用户 和 登录用户了,登录成功后会成功返回用户信息。但注册成功后,后端只会返回了当前的用户 ID,这里就不对了。注册成功后同样需要返回当前用户信息(用于页面的展示)
# 3.2、优化注册成功后,返回用户信息
TIP
当用户注册成功后,根据 ID 快速查询当前用户的信息,并返回给前端
在 cloud/login/index.js
中修改逻辑
// 云函数入口文件
const cloud = require("wx-server-sdk");
// 1、指定云函数的环境,注:使用云函数前必须指定环境进行初始化
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); // 使用当前云环境
// 云函数入口函数
// event 指:前端传给后端的字段
exports.main = async (event) => {
const wxContext = cloud.getWXContext();
// 解构前端传入的字段
const { avatarUrl, nickName } = event;
// 如果数据库中没有当前用户的信息(注册)
// 1、初始化数据库(获取数据库的引用)
const db = cloud.database();
// 2、指定集合(数据表),获取集合的引用
const users = db.collection("users");
// 在新增用户数据之前
// 3、先查询当前用户是否已注册过
const { data } = await users
.where({
_openId: wxContext.OPENID,
})
.get();
// 根据返回的 data 数组的长度来进行判断,是否已存在该用户
if (data.length === 0) {
// 等于 0 ,数据库中没有当前用户的信息(注册)
// 在数据库中新增用户数据(注册)
const res = await users.add({
data: {
avatarUrl,
nickName,
// 注册时所需要的其他字段信息也可以再次加入 ...
// 账户余额
money: 0,
// 注册时一定要加入 openid 作为唯一标识
_openId: wxContext.OPENID,
},
});
// ---------------------
// 当用户注册成功后,根据 ID 快速查询当前用户的信息,并返回给前端
// doc:接收 _id 快速返回该 id 的数据
const user = await users.doc(res._id).get();
// 注册成功后,返回当前用户数据
return {
data: user.data,
};
} else {
// 如果数据库中存在当前用户的信息(已注册过),则直接返回当前用户信息(登录)
// 登录成功后,返回当前用户数据
return {
data: data[0],
};
}
};
# 4、在小程序页面逻辑获取登录后的数据
TIP
- 在页面逻辑中获取登录后的用户数据
- 并将数据库中真实的用户信息同步到本地存储中,做数据的持久化
在 pages/me/me.js
中
// pages/me/me.js
// 获取应用的实例
const app = getApp();
Page({
// 页面的初始数据
data: {
userInfo: {},
hasUserInfo: false,
},
// 授权登录
async handleLogin(e) {
// 1、获取用户信息
const { userInfo } = await wx
.getUserProfile({
desc: "desc",
})
.catch((err) => {
console.log(err);
});
if (userInfo) {
// 实现用户注册(如果用户已注册,直接登录即可)
// 设置加载中的提示
// wx.showLoading({
// title: '正在授权中 ...',
// })
// 调用云函数
// 2、把当前用户信息提交给后端,用于创建生成一个用户账号(即:注册用户信息
// 也可以使用双重解构 将 res 替换成 {result:{data}}
const res = await wx.cloud.callFunction({
name: "login",
data: {
avatarUrl: userInfo.avatarUrl,
nickName: userInfo.nickName,
},
});
// console.log(res)
// 授权成功后
// 将用户信息存储在 本地存储中,用于用户下次进入小程序时,不必重复授权登录
app.setStorage("userInfo", res.result.data);
// 更新 data
this.setData({
// 更新 userInfo 的数据
userInfo: res.result.data,
hasUserInfo: true,
});
}
},
// 省略部分代码 ...
});
# 5、当页面切换时,实时获取最新的用户数据
在 pages/me/me.js
中
// 生命周期函数--监听页面显示
async onShow() {
// 获取本地存储中的用户信息
const res = await app.getStorage('userInfo')
.catch(err => {
console.log(err)
})
if(res){
// 如果 res 中有值,表示用户已经登录了
// 此时,需要请求当前用户数据库中的最新数据(与数据库信息保持同步)
const user = await wx.cloud.database().collection('users').doc(res._id).get()
console.log(user)
// 更新data
this.setData({
userInfo: user.data,
hasUserInfo: true
})
}
},
当页面切换时,控制台会报错,是因为数据库没有用户可读的权限
在云开发控制台中,修改数据库的权限即可
心语:
专注,就是将精力集中在某一个领域,消耗的少,聚焦的多,就容易在一个行业里站住脚,再积累几年,就生长出来了。
要紧的是果敢的迈出第一步,对与错都先不管,自古就没有设计好再开步的事儿,别想把一切都弄清楚再去走路。鲁莽者要学会思考,善思者要克服的是犹豫。目的可求完美,举步之际则无需周全。
生活不能等待别人来安排,要自己去争取和奋斗。不要质疑你现在写的一行行代码,做的一道道题,开发一个个项目,它终要把你送向更远的远方,你的使命是去看世界,成为更好的自己,这些都是替你打开世界的方式。
大厂最新技术学习分享群

微信扫一扫进群,获取资料
X