微信小程序开发周记(11.27-12.3)
第二周周报(部分)
云开发相关
云开发是管理微信小程序的后端数据库、运营数据等逻辑核心的平台。
前置前置知识
资源环境
用户开通云开发后即创建了一个独立的环境,包括数据库、存储空间、云函数等一整套云开发资源。实际开发中,建议每个正式环境都搭配一个测试环境,所有功能都在测试环境上测试完毕后迁移到正式环境。
账号
云开发所对应的账号权益相关。
概念引入
数据库
云开发提供了 JSON 数据库,每条记录是一个 JSON 格式对象。一个数据库有多个集合,集合可以看作一个 JSON 数组,数组中每个对象就是一条记录。
关系型数据库:
数据通常存储在一个二维表中,行是记录,列是属性;其他概念如下:
- 主键(Primary Key):用于唯一标识每一行记录的列。唯一,且不为空。
- 外键(Foreign Key):用于建立表和表之间关系的列,指向另一个表中的主键。
- 关系、SQL 等概念。
MySQL等都是常见的关系型数据库。
eg.图书记录
[ { "_id": "Wzh76lk5_O_dt0vO", "title": "Book 1", "author": "Null", "characters": [ "A", "B", "C" ], "publishInfo": { "year": 2023, "country": "" } }, { "_id": "Wzia0lk5_O_dt0vR", "_openid": "ohl4L0Rnhq7vmmbT_DaNQa4ePaz0", ... } ]其中,
_id是一条记录的唯一标志;其他字段都可以叫做 JSON 对象,可以是对象或数组;_openid标志记录的创建者(需要注意的是管理员创建的记录不会有此字段,如在管理端(控制台和云函数)创建的记录),开发者可以自行定义_id而不能自定义_openid。
API
数据库 API 分为小程序端和服务端两部分。小程序端 API 调用权限控制严格,开发者可在小程序内直接调用 进行非敏感数据操作;服务端 API 可以操作更高安全要求的数据,在云函数内操作,其环境与客户端完全隔离。
存储
云开发提供了一块存储空间,可以在小程序端和云函数端通过 API 使用。
eg. 在小程序内让用户选择一张图片,然后上传到云端管理:
// 让用户选择一张图片 wx.chooseImage({ success: chooseResult => { // 将图片上传至云存储空间 wx.cloud.uploadFile({ // 指定上传到的云路径 cloudPath: 'my-photo.png', // 指定要上传的文件的小程序临时文件路径 filePath: chooseResult.tempFilePaths[0], // 成功回调 success: res => { console.log('上传成功', res) }, }) }, })
wx.chooseImage是微信小程序提供的 API,允许用户从手机相册中选择一张图片。此处我们传入一个对象,包含一个success回调函数。这个函数会在用户成功选择图片后被调用,选择的图片信息会以参数的形式传递给chooseResult。接下来,在
wx.cloud.uploadFile函数中有一些参数。其中filePath参数指要上传的文件的小程序临时路径,从chooseResult中获取。
云函数
云函数指一段运行在云端的代码。需要注意的是此时在小程序等端口调用这些函数需要创建请求。
故在云函数中,要获得每次请求的上下文信息(如 appid、openid 等)。这可以使用 wx-server-sdk 提供的 getWXContext 方法。
eg. 定义一个名字为 add 的云函数,功能是将传入的两个参数 a 和 b 相加:
// index.js 是入口文件,云函数被调用时会执行该文件导出的 main 方法 // event 包含了调用端(小程序端)调用该函数时传过来的参数,同时还包含了可以通过 getWXContext 方法获取的用户登录态 `openId` 和小程序 `appId` 信息 const cloud = require('wx-server-sdk') exports.main = async (event, context) => { let { userInfo, a, b} = event let { OPENID, APPID } = cloud.getWXContext() // 这里获取到的 openId 和 appId 是可信的 let sum = a + b return { OPENID, APPID, sum } }在开发者工具上传云函数后,在小程序中的调用方法:
wx.cloud.callFunction({ // 需调用的云函数名 name: 'add', // 传给云函数的参数 data: { a: 12, b: 19, }, // 成功回调 complete: console.log }) // 当然 promise 方式也是支持的 wx.cloud.callFunction({ name: 'add', data: { a: 12, b: 19 } }).then(console.log)
云能力
云能力是指通过云服务实现的各种功能和服务,比如存储、数据库等。通过 wx.cloud.init() 方法可以初始云开发能力。在初始化时传递一些配置参数。如 traceUser 参数传值为 true 时表示开启用户访问记录,记录用户访问小程序的时间等信息。
开发指引
云开发控制台
云开发提供了一个控制台用于可视化管理云资源。控制台包含以下几大模块。
- 概览:查看云资源的总体使用情况
- 用户管理:查看小程序的用户访问记录
- 数据库:管理数据库集合、记录、权限设置、索引设置
- 存储管理:管理云文件、权限设置
- 云函数:管理云函数、查看调用日志、监控记录
- 统计分析:查看云资源详细使用统计
若小程序由多名开发者共同维护,小程序管理员可以在控制台权限设置中配置相应开发者权限。
在用户管理中会显示使用云能力的小程序的访问用户列表,默认以访问时间倒序排列,访问时间的触发点是在小程序端调用 wx.cloud.init 方法,且其中的 traceUser 参数传值为 true。例:
wx.cloud.init({
env: 'test-123',
traceUser: true,
})
云能力初始化
什么是云能力初始化?为什么要进行云能力初始化?云能力初始化是指在使用云开发服务的过程中,进行一系列的初始化操作,以便在小程序端和云函数端启用云能力。通常包括配置参数、链接到云服务、开启特定功能等。在微信小程序的云开发中,云能力初始化通常是调用 wx.cloud.init 方法来完成的。
云能力初始化是确保小程序与云服务正常通信的关键步骤。
小程序端初始化
在小程序端开始使用云能力前,需要先调用 wx.cloud.init() 方法完成云能力初始化(需要先开通云服务)。定义如下:
function init(options): void
//接受一个可选的 option 参数,没有返回值;函数只能调用一次,多次调用只有第一次生效。
option 相关配置:
字段 数据类型 必填 默认值 说明 env string | object 是 后续 API 调用的默认环境配置,传入字符串形式的环境 ID 可以指定所有服务的默认环境,传入对象可以分别指定各个服务的默认环境,见下方详细定义 traceUser boolean 否 false 是否在将用户访问记录到用户管理中,在控制台中可见 当
env传入参数为对象时,可以指定各个服务的默认环境,可选字段如下:
字段 数据类型 必填 默认值 说明 database string 否 空 数据库 API 默认环境配置 storage string 否 空 存储 API 默认环境配置 functions string 否 空 云函数 API 默认环境配置
通常,我们会在 app.js 中进行初始化操作。
云函数端初始化
cloud.init() 定义如下:
function init(options): void
类似小程序端,稍有不同。
云数据库
上手
包括对集合中数据的操作。
集合
微信数据库的架构为:集合 \(\rightarrow\) 记录 \(\rightarrow\) 字段。
新建集合后即可在集合内部添加记录。
数据类型
String Number Object Array Bool Date Geo(多种地理位置类型) Null
Date
精确到毫秒,小程序端可以使用 JavaScript 内置的 Date 对象来创建。
需要注意的是客户端事件和服务端事件有一定区别。要使用服务端时间,应该用 API 中提供的 serverDate 对象来创建一个服务器当前时间的标记。
当使用了
serverDate对象的请求抵达服务端处理时,该字段会被转换成服务端当前的时间,更棒的是,我们在构造serverDate对象时还可通过传入一个有offset字段的对象来标记一个与当前服务端时间偏移offset毫秒的时间,这样我们就可以达到比如如下效果:指定一个字段为服务端时间往后一个小时。
地理位置
支持的地理位置包括:点(Point)、线段(LineString)、多边形(Polygon)、点集/线段集/多边形集(Multi-)
Null
相当于一个占位符,标识空的但存在的值。
权限相关
数据库的权限分为小程序端和管理端。管理端包括云函数端和控制台。小程序端就指的是用户使用界面,读写数据库受权限控制限制;管理端运行在云函数上,云控制台的权限同管理端,拥有所有权限。
- 小程序端
- 管理端
- 云函数(管理端的运行场所)
- 云控制台(权限同管理端)
增删查改(SDK)
什么是 SDK?
初始化
在使用数据库 API 进行操作前,需要引用数据库。
const db = wx.cloud.database()
//获取默认环境的数据库引用
const testDB = wx.cloud.database({
env: 'test'
})
//获取名为 test 的环境的数据库引用
在获取数据库的引用之后,就可以用 collection 方法获得集合的引用。
const todos = db.collection('todos')
获取集合的引用并不会发起网络请求去拉取其数据,而是在我们实际进行操作时再进一步获取数据。除此之外,还可以通过集合上的 doc 方法来获取集合中一个指定 ID 的记录的引用。
插入
可以在集合对象上调用 add 方法往集合中插入记录。
todos.add({
data:{
description: "123",
due: new Date("2023-12-04"),
done: false
},
success: function(res) {
console.log(res)
}
})
//promise 风格
todos.add({
data:{
description: "123",
due: new Date("2023-12-04"),
done: false
}
})
.then(res => {
console.log(res)
})
查询
在记录和集合上都可以通过 get 方法获取数据,包括单个记录和集合中多个记录的数据。
可以使用 doc 获取记录。doc 通过 ID 来查找记录。
db.collection('todos').doc('b751f280656d6b96021655d3466053d9').get({
success: function(res) {
console.log(res.data)
}
})
//promise 风格
db.collection('todos').doc('b751f280656d6b96021655d3466053d9').get().then(res => {
console.log(res.data)
})
也可以一次性获得多条记录:
//eg.1
db.collection('todos').where({
_openid: 'usr-open-id',
done: false
}).get({
success: function(res) {
// res.data 是包含以上两条定义的记录的数组
console.log(res.data)
}
})
//eg.2_1
db.collection('todos').where({
_openid: 'user-open-id',
style: {
color: 'yellow'
}
})
.get({
success: function(res) {
console.log(res.data)
}
})
//eg.2_2
db.collection('todos').where({
_openid: 'user-open-id',
'style.color': 'yellow'
})
.get({
success: function(res) {
console.log(res.data)
}
})
//可以看到这里使用嵌套字段和点表示法都可以。具体来说,只要满足所有给出的条件即可。
甚至一次性获取一个集合的记录。不过通常不建议这么使用,因为要尽量避免一次性获取过量的数据。默认并且最多只返回 20 条记录,在云函数端这个数字则是 100 。可以使用 limit 限制数量,但仍不能超过最大值 20 或 100 。
db.collection('todos').get({
success: function(res) {
// res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条
console.log(res.data)
}
})
此时需要分批请求:
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
const MAX_LIMIT = 100
exports.main = async (event, context) => {
// 先取出集合记录总数
const countResult = await db.collection('todos').count()
const total = countResult.total
// 计算需分几次取
const batchTimes = Math.ceil(total / 100)
// 承载所有读操作的 promise 的数组
const tasks = []
for (let i = 0; i < batchTimes; i++) {
const promise = db.collection('todos').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
tasks.push(promise)
}
// 等待所有
return (await Promise.all(tasks)).reduce((acc, cur) => {
return {
data: acc.data.concat(cur.data),
errMsg: acc.errMsg,
}
})
}
浙公网安备 33010602011771号