所有内容都是微信小程序文档里面的,手动记录总结只为留下更深的记忆,有兴趣可以通读:小程序云开发官方文档
小程序云开发解放了开发者搭建服务器和运维的困扰,同时使用云开发进行核心业务开发能实现快速上线和迭代(和开发者已经使用的服务器兼容),它提供了三大基础能力支持:
- 云函数: 可以在云端运行的代码,开发者只需编写自身业务逻辑代码
- 数据库: 好像是MongoDB的简版,是可以在云函数中读写的JSON数据库
- 存储: 可以在小程序前端直接上传/下载文件,在云开发控制台管理
在小程序端开始使用云能力前,需先调用 wx.cloud.init
方法完成云能力初始化(注意小程序需先开通云服务,开通的方法是点击工具栏左上角的 “控制台” 按钮)。
wx.cloud.init({
env: 'test-x1dzi'
})
复制代码
env
的值是默认环境配置,传入字符串形式的环境 ID 可以指定所有服务的默认环境,传入对象可以分别指定各个服务的默认环境
数据库
数据类型
- String:字符串
- Number:数字
- Object:对象
- Array:数组
- Bool:布尔值
- GeoPoint:地理位置点
- Date:时间
- Null
权限控制
数据库的权限分为小程序端和管理端,管理端包括云函数端和控制台。小程序端运行在小程序中,读写数据库受权限控制限制,管理端运行在云函数上,拥有所有读写数据库的权限。云控制台的权限同管理端,拥有所有权限。
以下是开发了几种权限配置,由宽到紧排列:
- 仅创建者可写,所有人可读:数据只有创建者可写、所有人可读;比如文章。
- 仅创建者可读写:数据只有创建者可读写,其他用户不可读写;比如用私密相册。
- 仅管理端可写,所有人可读:该数据只有管理端可写,所有人可读;如商品信息。
- 仅管理端可读写:该数据只有管理端可读写;如后台用的不暴露的数据。
初始化
在开始使用数据库 API 进行增删改查操作之前,需要先获取数据库的引用。如:
const db = wx.cloud.database()
复制代码
如需指定引用某个数据库,假设一个环境名为test
,如下 获取:
const testDB = wx.cloud.database({
env: 'test'
})
复制代码
同样要操作一个集合,也要获取它的引用,通过collection
方法,比如获取待办事项清单集合:
const todos = db.collection('todos')
复制代码
获取集合的引用不会发起网络请求拉取它的数据,我们可以通过此引用在该集合上进行增删查改的操作。除此之外,还可以通过集合上的 doc 方法来获取集合中一个指定 ID 的记录的引用。同理,记录的引用可以用于对特定记录进行更新和删除操作。
通过doc
访求获取一个待办事项ID为 todo-test1
的引用:
const todo = db.collection('todos').doc('todo-test1')
复制代码
插入数据
通过在集合对象上调用add
方法向集合中插入一条记录,如新增一个待办事项:
db.collection('todos').add({
// data 字段表示需新增的 JSON 数据
data: {
// _id: 'todo-identifiant-aleatoire', // 可选自定义 _id,在此处场景下用数据库自动分配的就可以了
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
// 为待办事项添加一个地理位置(113°E,23°N)
location: new db.Geo.Point(113, 23),
done: false
},
success: function(res) {
// res 是一个对象,其中有 _id 字段标记刚创建的记录的 id
console.log(res + '成功插入')
}
})
复制代码
Promise 风格
db.collection('todos').add({
// data 字段表示需新增的 JSON 数据
data: {
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
location: new db.Geo.Point(113, 23),
done: false
}
})
.then(res => {
console.log(res + '成功插入')
})
复制代码
读取数据
通过get
方法猎取单个记录或集合中多个记录的数据,如下面是一个集合todos
的记录:
[
{
_id: 'todo-identifiant-aleatoire',
_openid: 'user-open-id', // 假设用户的 openid 为 user-open-id
description: "learn cloud database",
due: Date("2018-09-01"),
progress: 20,
tags: [
"cloud",
"database"
],
style: {
color: 'white',
size: 'large'
},
location: Point(113.33, 23.33), // 113.33°E,23.33°N
done: false
},
{
_id: 'todo-identifiant-aleatoire-2',
_openid: 'user-open-id', // 假设用户的 openid 为 user-open-id
description: "write a novel",
due: Date("2018-12-25"),
progress: 50,
tags: [
"writing"
],
style: {
color: 'yellow',
size: 'normal'
},
location: Point(113.22, 23.22), // 113.22°E,23.22°N
done: false
}
// more...
]
复制代码
获取一个记录的数据,通过ID,调用get方法:
db.collection('todos').doc('todo-identifiant-aleatoire').get().then(res => {
// res.data 包含该记录的数据
console.log(res.data)
})
复制代码
获取多个记录的数据,通过调用集合上的where
方法指定查询条件,再调用get
方法:
db.collection('todos').where({
_openid: 'user-open-id',
done: false
})
.get({
success: function(res) {
// res.data 是包含以上定义的两条记录的数组
console.log(res.data)
}
})
复制代码
获取一个集合的数据
我们可以直接在一个集合上调用get
方法获取它所有记录,不过要尽量避免一次性获取过量的数据,小程序端获取集合数据默认并且最多返回20条记录,云函数端是100,我们可以通过limit
方法指定需要获取的记录数量。
db.collection('todos').get().then(res => {
// res.data 是一个包含集合中有权限访问的所有记录的数据,不超过 20 条
console.log(res.data)
})
复制代码
在云函数端获取一个集合所有记录的盒子,因为云函数端最多一次取100条的限制,所以我们要分批取:
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,
}
})
}
复制代码
构建查询条件
使用数据库 API 提供的 where
方法我们可以构造复杂的查询条件完成复杂的查询任务。
假设我们需要查询进度大于 30% 的待办事项,那么传入对象表示全等匹配的方式就无法满足了,这时就需要用到查询指令。数据库 API 提供了大于、小于等多种查询指令,这些指令都暴露在 db.command
对象上。比如查询进度大于 30% 的待办事项:
const _ = db.command
db.collection('todos').where({
// gt 方法用于指定一个 "大于" 条件,此处 _.gt(30) 是一个 "大于 30" 的条件
progress: _.gt(30)
})
.get({
success: function(res) {
console.log(res.data)
}
})
复制代码
查询指令 | 说明 |
---|---|
eq | 等于 |
neq | 不等于 |
lt | 小于 |
lte | 小于或等于 |
gt | 大于 |
gte | 大于或等于 |
in | 字段值在给定数组中 |
nin | 字段值不在给定数组中 |
还有逻辑指令,用于指定一个字段需要同时满足多个条件,如and
逻辑指令查询进度在 30% 和 70% 之间的待办事项:
const _ = db.command
db.collection('todos').where({
// and 方法用于指定一个 "与" 条件,此处表示需同时满足 _.gt(30) 和 _.lt(70) 两个条件
progress: _.gt(30).and(_.lt(70))
})
.get({
success: function(res) {
console.log(res.data)
}
})
复制代码
or
查询进度为 0 或 100 的待办事项:
const _ = db.command
db.collection('todos').where({
// or 方法用于指定一个 "或" 条件,此处表示需满足 _.eq(0) 或 _.eq(100)
progress: _.eq(0).or(_.eq(100))
})
.get({
success: function(res) {
console.log(res.data)
}
})
复制代码
更新数据
更新数据主要有两个方法:
API | 说明 |
---|---|
update | 局部更新一个或多个记录 |
set | 替换更新一个记录 |
局部更新
使用 update
方法可以局部更新一个记录或一个集合中的记录,局部更新意味着只有指定的字段会得到更新,其他字段不受影响。
例如通过一个ID来将一个待办事项置为已完成:
db.collection('todos').doc('todo-identifiant-aleatoire').update({
// data 传入需要局部更新的数据
data: {
// 表示将 done 字段置为 true
done: true
},
success: function(res) {
console.log(res.data)
}
})
复制代码
替换更新
使用set
方法替换更新指定的记录:
const _ = db.command
db.collection('todos').doc('todo-identifiant-aleatoire').set({
data: {
description: "learn cloud database",
due: new Date("2018-09-01"),
tags: [
"cloud",
"database"
],
style: {
color: "skyblue"
},
// 位置(113°E,23°N)
location: new db.Geo.Point(113, 23),
done: false
},
success: function(res) {
console.log(res.data)
}
})
复制代码
指定ID的记录不存在,则会自动创建该记录。
删除数据
使用remove
方法删除一条记录:
db.collection('todos').doc('todo-identifiant-aleatoire').remove({
success: function(res) {
console.log(res.data)
}
})
复制代码
删除多条数据
可以通过where
语句选取多条记录执行删除(需要使用 Server 端的云函数),只有有权限删除的记录会被删除,下面是删除所有已完成的待办事项:
// 使用了 async await 语法
const cloud = require('wx-server-sdk')
const db = cloud.database()
const _ = db.command
exports.main = async (event, context) => {
try {
return await db.collection('todos').where({
done: true
}).remove()
} catch(e) {
console.error(e)
}
}
复制代码
索引管理
建立索引是保证数据库性能、保证小程序体验的重要手段。我们应为所有需要成为查询条件的字段建立索引。建立索引的入口在控制台中,可分别对各个集合的字段添加索引。
存储
- 云存储提供高可用、高稳定、强安全的云端存储服务,支持任意数量和形式的非结构化数据存储,如视频和图片,并在控制台进行可视化管理。云存储包含以下功能:
- 存储管理:支持文件夹,方便文件归类。支持文件的上传、删除、移动、下载、搜索等,并可以查看文件的详情信息 权限设置:可以灵活设置哪些用户是否可以读写该文件夹中的文件,以保证业务的数据安全
- 上传管理:在这里可以查看文件上传历史、进度及状态 文件搜索:支持文件前缀名称及子目录文件的搜索
- 组件支持:支持在 image、audio 等组件中传入云文件 ID
云函数
云函数即在云端(服务器端)运行的函数。在物理设计上,一个云函数可由多个文件组成,占用一定量的 CPU 内存等计算资源;各云函数完全独立;可分别部署在不同的地区。开发者无需购买、搭建服务器,只需编写函数代码并部署到云端即可在小程序端调用,同时云函数之间也可互相调用。
我的第一个云函数
定义一个将两个数字相加的函数示例:
在项目根目录找到 project.config.json
文件,新增 cloudfunctionRoot
字段,指定本地已存在的目录作为云函数的本地根目录
{
"cloudfunctionRoot": "./functions/"
}
复制代码
设置完成后,云函数的根目录的图标会变成 “云目录图标”,云函数根目录下的第一级目录(云函数目录)是与云函数名字相同的,如果对应的线上环境存在该云函数,则我们会用一个特殊的 “云图标” 标明
接着,我们在云函数根目录上右键,在右键菜单中,可以选择创建一个新的 Node.js 云函数,我们将该云函数命名为 add
。开发者工具在本地创建出云函数目录和入口 index.js
文件,同时在线上环境中创建出对应的云函数。创建成功后,工具会提示是否立即本地安装依赖,确定后工具会自动安装 wx-server-sdk
。我们可以看到类似如下的一个云函数模板:
const cloud = require('wx-server-sdk')
// 云函数入口函数
exports.main = async (event, context) => {
}
复制代码
当小程序端调用云函数时,event
就是小程序端调用云函数时传入的参数,外加后端自动注入的小程序用户的 openid
和小程序的 appid
。context
对象包含了此处调用的调用信息和运行状态,可以用它来了解服务运行的情况。
填充模板:
exports.main = async (event, context) => {
return {
sum: event.a + event.b
}
}
复制代码
将传入的 a 和 b 相加并作为 sum 字段返回给调用端。
调用云函数
wx.cloud.callFunction({
// 云函数名称
name: 'add',
// 传给云函数的参数
data: {
a: 1,
b: 2,
},
})
.then(res => {
console.log(res.result) // 3
})
.catch(console.error)
复制代码
获取小程序用户信息
当小程序端调用云函数时,云函数的传入参数中会被注入小程序端用户的 openid,开发者可以直接使用该 openid。
从小程序端调用云函数时,云函数的第一个参数 event 会被注入一个 userInfo 对象,其中含有 openId 字段和 appId 字段,可以写这么一个云函数进行测试:
// index.js
exports.main = (event, context) => {
return event.userInfo
}
// 调用
wx.cloud.callFunction({
name: 'test',
complete: res => {
console.log('callFunction test result: ', res)
}
})
复制代码
输出的对象结构:
{
"appId": "xxx",
"openId": "yyy"
}
复制代码
异步返回结果
使用 Promise
方法来完成
// index.js
exports.main = async (event, context) => {
return new Promise((resolve, reject) => {
// 在 3 秒后返回结果给调用方(小程序 / 其他云函数)
setTimeout(() => {
resolve(event.a + event.b)
}, 3000)
})
}
// 在小程序代码中:
wx.cloud.callFunction({
name: 'test',
data: {
a: 1,
b: 2,
},
complete: res => {
console.log('callFunction test result: ', res)
},
})
复制代码
在云函数中使用 wx-server-sdk
使用前都需要执行一次初始化方法:
const cloud = require('wx-server-sdk')
// 默认配置
cloud.init()
// 或者传入自定义配置
cloud.init({
env: 'some-env-id'
})
复制代码
云函数中调用数据库
假设在数据库中已有一个 todos
集合,我们可以如下方式取得 todos
集合的数据:
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
exports.main = async (event, context) => {
// collection 上的 get 方法会返回一个 Promise,因此云函数会在数据库异步取完数据后返回结果
return db.collection('todos').get()
}
复制代码
云函数中调用存储
假设我们要上传在云函数目录中包含的一个图片文件(demo.jpg):
const cloud = require('wx-server-sdk')
const fs = require('fs')
const path = require('path')
exports.main = async (event, context) => {
const fileStream = fs.createReadStream(path.join(__dirname, 'demo.jpg'))
return await cloud.uploadFile({
cloudPath: 'demo.jpg',
fileContent: fileStream,
})
}
复制代码
云函数中调用其他云函数
假设我们要在云函数中调用另一个云函数 sum 并返回 sum 所返回的结果:
const cloud = require('wx-server-sdk')
exports.main = async (event, context) => {
return await cloud.callFunction({
name: 'sum',
data: {
x: 1,
y: 2,
}
})
}
复制代码