目录
云开发是什么
微信联合腾讯云提供的原生 ServerLess 云服务
与传统开发模式的区别:
云开发数据存储能力
// 一行代码创建数据
db.collection('todos').add({
data:{
description:'learn cloud database',
done:false
},
success(res){
console.log(res)
}
})
// 一行代码查询数据
db.collection('todos').doc(
'todo-identifiant-aleatoire'
).get({
success(res){
console.log(res)
}
})
云开发的文件存储能力
云开发的计算能力
云开发API
分类:小程序端、服务器端;数据存储 API、文件存储 API、云函数 API
云开发准备工作
配置环境:
- 点击云服务申请环境 ID
- 右键云函数根目录,选择环境
- 在几处关键位置添加环境 ID
下载NodeJS
NodeJS 是在服务端运行 JavaScript 的运行环境,云开发所使用的服务端环境就是 NodeJS 。npm 是 Node 包管理器,通过 npm,我们可以非常方便的安装云开发所需要的依赖包。下载链接:
打开终端输入
node --version
npm --version
检查是否安装成功。
上传云函数:
选择 cloudfunctions 下的云函数文件夹,右键复制路径,在 cmd 中输入
cd /D 路径
进入文件夹,输入
npm install
下载依赖,下载成功后右键文件夹选择“上传并部署:所有文件”就可以部署了。可以在云服务查看状态。
为什么要在云函数目录执行 npm install,而不是其他地方?这是因为 npm install 会下载云函数目录下的配置文件 package.json 里的 dependencies,它表示的是当前云函数需要依赖的模块。package.json 在哪里,就在哪里执行 npm install,没有 package.json,没有 dependencies,就没法下载啊。
node_modules 文件夹这么大(几十 M~几百 M 都可能),会不会影响小程序的大小?小程序的大小只与 miniprogram 文件夹有关,当你把云函数都部署上传到服务器之后,你把整个 cloudfuntions 文件夹删掉都没有关系。相同的依赖(比如都依赖 wx-server-sdk)一旦部署到云函数之后,你可以选择不上传 node_modules 时,因为已经上传过了。
数据库操作
云控制台导入数据,支持用JSON格式文件:
{
// 描述,String 类型
"description": "learn mini-program cloud service",
// 截止时间,Date 类型
"due": Date("2018-09-01"),
// 标签,Array 类型
"tags": [
"tech",
"mini-program",
"cloud"
],
// 个性化样式,Object 类型
"style": {
"color": "red"
},
// 是否已完成,Boolean 类型
"done": false
}
初始化
在开始使用数据库 API 进行增删改查操作之前,需要先获取数据库的引用。
const db = wx.cloud.database(); // 默认环境调用一个数据库引用
const testDB = wx.cloud.database({
env: 'test'
}); // 指定环境调用数据库引用
获取需要操作的表
const todos = db.collection('todos');
获取指定 ID 对应记录
const todo = db.collection('todos').doc('todo-identifiant-aleatoire');
// todo-identifiant-aleatoire 即ID号
增删改查
add
添加数据用 add 方法
在 data 字段中以 JSON 格式添加
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)
}
})
get
查询用 get 方法
为了防止误操作以及保护小程序体验,小程序端在获取集合数据时服务器一次默认并且最多返回 20 条记录,云函数端这个数字则是 100。开发者可以通过 limit 方法指定需要获取的记录数量,但小程序端不能超过 20 条,云函数端不能超过 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,
}
})
}
where
where 方法可以指定查询条件
db.collection('todos').where({
_openid: 'user-open-id',
done: false
})
.get().then(res=>{
console.log(res)
});
查询嵌套字段时
db.collection('todos').where({
_openid: 'user-open-id',
style: {
color: 'yellow'
}
})
.get().then();
db.collection('todos').where({
_openid: 'user-open-id',
'style.color': 'yellow'
})
.get().then();
查询指令
数据库查询API大都暴露在 db.command 对象上
const _ = db.command
db.collection('todos').where({
// gt 方法用于指定一个 "大于" 条件
// 此处 _.gt(30) 是一个 "大于 30" 的条件
progress: _.gt(30)
})
.get().then();
逻辑操作符
and
查询操作符,用于表示逻辑 "与" 的关系,表示需同时满足多个查询筛选条件
参数形式:多个参数/数组形式
如果仅表示同时满足提供的多个完整查询条件,不需要用到 and
const _ = db.command
db.collection('todo').where({
progress: _.gt(50),
tags: 'cloud'
}).get()
通常需要显示使用 and 是用在有跨字段或操作的时候,如以下表示 “progress 字段大于 50 或 tags 字段等于 cloud 或 tags 数组字段(如果 tags 是数组)中含有 cloud”:
const _ = db.command
db.collection('todo').where(_.and([
_.or({
progress: _.gt(50)
}),
_.or({
tags: 'cloud'
})
])).get()
command 支持链式调用:
const _ = db.command
db.collection('todo').where({
progress: _.gt(50).lt(100)
})
or
查询操作符,用于表示逻辑 "或" 的关系,表示需同时满足多个查询筛选条件。或指令有两种用法,一是可以进行字段值的 “或” 操作,二是也可以进行跨字段的 “或” 操作。
参数形式:多个参数/数组形式
如筛选出进度大于 80 或小于 20 的 todo:
// 流式写法
const _ = db.command
db.collection('todo').where({
progress: _.gt(80).or(_.lt(20))
})
// 前置写法1
const _ = db.command
db.collection('todo').where({
progress: _.or(_.gt(80), _.lt(20))
})
// 前置写法2
const _ = db.command
db.collection('todo').where({
progress: _.or([_.gt(80), _.lt(20)])
})
not
查询操作符,用于表示逻辑 "非" 的关系,表示需不满足指定的条件。
通常与其他操作符搭配使用
const _ = db.command
db.collection('todo').where({
progress: _.not(_.eq(100))
})
nor
查询操作符,用于表示逻辑 "都不" 的关系,表示需不满足指定的所有条件。如果记录中没有对应的字段,则默认满足条件。
筛选出进度既不小于20又不大于80的 todo :
const _ = db.command
db.collection('todo').where({
progress: _.nor([_.lt(20), _.gt(80)])
})
比较操作符
eq
查询筛选条件,表示字段等于某个值。eq 指令接受一个字面量 (literal),可以是 number, boolean, string,object, array, Date。
// 这种写法表示匹配 stat.publishYear == 2018 且 stat.language == 'zh-CN'
db.collection('articles').where({
stat: {
publishYear: 2018,
language: 'zh-CN'
}
})
// 这种写法表示 stat 对象等于 { publishYear: 2018, language: 'zh-CN' }
const _ = db.command
db.collection('articles').where({
stat: _.eq({
publishYear: 2018,
language: 'zh-CN'
})
})
neq
表示字段不等于某个值,与 eq 相反
lt
查询筛选操作符,表示需小于指定值。可以传入 Date 对象用于进行日期比较。
const _ = db.command
db.collection('todos').where({
progress: _.lt(50)
})
.get({
success: console.log,
fail: console.error
})
lte
查询筛选操作符,表示需小于或等于指定值。
gt
查询筛选操作符,表示需大于指定值。
gte
查询筛选操作符,表示需大于或等于指定值。
in
查询筛选操作符,表示要求值在给定的数组内。
找出进度为 0 或 100 的 todo:
const _ = db.command
db.collection('todos').where({
progress: _.in([0, 100])
})
.get({
success: console.log,
fail: console.error
})
nin
查询筛选操作符,表示要求值不在给定的数组内。
字段操作符
exist
判断字段是否存在
找出存在 tags 字段的记录
const _ = db.command
db.collection('todos').where({
tags: _.exists(true)
})
.get({
success: console.log,
fail: console.error
})
数组操作符
all
数组查询操作符。用于数组字段的查询筛选条件,要求数组字段中包含给定数组的所有元素。
找出 tags 数组字段同时包含 cloud 和 database 的记录:
const _ = db.command
db.collection('todos').where({
tags: _.all(['cloud', 'database'])
})
.get({
success: console.log,
fail: console.error
})
如果数组元素是对象,则可以用 _.elemMatch 匹配对象的部分字段。假设有字段 places 定义如下:
{
"type": string
"area": number
"age": number
}
找出数组字段中至少同时包含一个满足 “area 大于 100 且 age 小于 2” 的元素和一个满足 “type 为 mall 且 age 大于 5” 的元素
const _ = db.command
db.collection('todos').where({
places: _.all([
_.elemMatch({
area: _.gt(100),
age: _.lt(2),
}),
_.elemMatch({
type: 'mall',
age: _.gt(5),
}),
]),
})
.get({
success: console.log,
fail: console.error,
})
elemMatch
用于数组字段的查询筛选条件,要求数组中包含至少一个满足 elemMatch 给定的所有条件的元素
数组是对象数组的情况:
{
"_id": "a0",
"city": "x0",
"places": [{
"type": "garden",
"area": 300,
"age": 1
}, {
"type": "theatre",
"area": 50,
"age": 15
}]
}
找出 places 数组字段中至少同时包含一个满足 “area 大于 100 且 age 小于 2” 的元素
const _ = db.command
db.collection('todos').where({
places: _.elemMatch({
area: _.gt(100),
age: _.lt(2),
})
})
.get()
如果不使用 elemMatch 而直接如下指定条件,则表示的是 places 数组字段中至少有一个元素的 area 字段大于 100 且 places 数组字段中至少有一个元素的 age 字段小于 2:
const _ = db.command
db.collection('todos').where({
places: {
area: _.gt(100),
age: _.lt(2),
}
})
.get()
数组元素是普通数据类型的情况:
集合示例:
{
"_id": "a0",
"scores": [60, 80, 90]
}
找出 scores 数组字段中至少同时包含一个满足 “大于 80 且小于 100” 的元素
const _ = db.command
db.collection('todos').where({
scores: _.elemMatch(_.gt(80).lt(100))
})
.get()
size
更新操作符,用于数组字段的查询筛选条件,要求数组长度为给定值
const _ = db.command
db.collection('todos').where({
places: _.size(2)
})
.get({
success: console.log,
fail: console.error,
})
更新数据
更新数据主要有 set 和 update 方法
使用 update 方法可以局部更新一个记录或一个集合中的记录,局部更新意味着只有指定的字段会得到更新,其他字段不受影响
db.collection('todos').doc('todo-identifiant-aleatoire').update({
// data 传入需要局部更新的数据
data: {
// 表示将 done 字段置为 true
done: true
}).then();
如果需要更新多个数据,需在 Server 端进行操作(云函数),在 where 语句后同样的调用 update 方法即可,比如将所有未完待办事项的进度加 10%:
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: false
})
.update({
data: {
progress: _.inc(10)
},
})
} catch(e) {
console.error(e)
}
}
删除数据
对记录使用 remove 方法可以删除该条记录
db.collection('todos').doc('todo-identifiant-aleatoire').remove().then();
如果需要更新多个数据,需在 Server 端进行操作(云函数)。可通过 where 语句选取多条记录执行删除,只有有权限删除的记录会被删除
// 使用了 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)
}
}
普通匹配
传入的对象的每个 < key , value > 构成一个筛选条件,有多个 < key , value > 则表示需同时满足这些条件
db.collection('todos').where({
done: false,
progress: 50
}).get().then();
查询、更新数组/嵌套对象 | 微信开放文档 (qq.com)
collection
where
指定查询条件,返回带新查询条件的新的集合引用
- 参数 condition:Object
- 返回值 Collection
const _ = db.command
const result = await db.collection('todos').where({
price: _.lt(100)
}).get()
limit
指定查询结果集数量上限
在小程序端默认及最大上限为 20,在云函数端默认及最大上限为 1000
- 参数 value:number
- 返回值 Collection
db.collection('todos').limit(10)
.get()
.then(console.log)
.catch(console.error)
orderBy
指定查询排序条件
- 参数 fieldPath:string
- 参数 String:order
- 返回值 Collection
方法接受一个必填字符串参数 fieldName 用于定义需要排序的字段,一个字符串参数 order 定义排序顺序。order 只能取 asc 或 desc。
如果需要对嵌套字段排序,需要用 "点表示法" 连接嵌套字段,比如 style.color 表示字段 style 里的嵌套字段 color 。
同时也支持按多个字段排序,多次调用 orderBy 即可,多字段排序时的顺序会按照 orderBy 调用顺序先后对多个字段排序
db.collection('todos')
.orderBy('progress', 'desc')
.orderBy('description', 'asc')
.get()
.then(console.log)
.catch(console.error)
skip
指定查询返回结果时从指定序列后的结果开始返回,常用于分页
- 参数 offset: number
- 返回值 Collection
db.collection('todos').skip(10)
.get()
.then(console.log)
.catch(console.error)
field
指定返回结果中记录需返回的字段
- 参数 projection:Object
- 返回值 Collection
db.collection('todos').field({
description: true,
done: true,
progress: true,
})
.get()
.then(console.log)
.catch(console.error)
count
统计匹配查询条件的记录的条数
db.collection('demolist').count().then(res => {
console.log(res);
});
watch
监听集合中符合查询条件的数据的更新事件。使用 watch 时,支持 where, orderBy, limit,不支持 field。
- 参数 onChange:function
- 参数 onErr:function
- 返回值 Watcher 对象
官方示例:
const db = wx.cloud.database()
const watcher = db.collection('todos').where({
_openid: 'xxx' // 填入当前用户 openid
}).watch({
onChange: function(snapshot) {
console.log('snapshot', snapshot)
},
onError: function(err) {
console.error('the watch closed because of error', err)
}
})
// ...
// 关闭
await watcher.close()
实战示例:
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getData();
db.collection('demolist').watch({
onChange: res => {
console.log(res);
},
onError: err => {
console.log(err);
}
})
}
控制台数据库高级操作
从开发者工具 1.02.1906202 开始,在云控制台数据库管理页中可以编写和执行数据库脚本,脚本可对数据库进行增删查改 & 聚合操作,语法同 SDK 数据库语法
商品订单管理示例
数据库安全规则 | 微信开放文档 (qq.com)
// shop 商店集合
{
_id: string,
name: string, // 商店名
location: GeoPoint, // 商店地理位置
owner: string, // 商店拥有者 openid
managers: string[], // 商店管理员 openid 列表
}
// item 商品集合
{
_id: string,
shopId: string, // 所在商店
name: string, // 商品名
price: number, // 价格
stock: number, // 库存
}
// order 订单集合
{
_id: string,
shopId: string, // 下单的商店
itemId: string, // 下单的商品
price: number, // 成交价格
amount: number, // 成交数量
status: string, // 状态
createTime: Date, // 创建时间
updateTime: Date, // 更新时间
}
云函数
云函数即在云端(服务器端)运行的函数。在物理设计上,一个云函数可由多个文件组成,占用一定量的 CPU 内存等计算资源;各云函数完全独立;可分别部署在不同的地区。开发者无需购买、搭建服务器,只需编写函数代码并部署到云端即可在小程序端调用,同时云函数之间也可互相调用。
一个云函数的写法与一个在本地定义的 JavaScript 方法无异,代码运行在云端 Node.js 中。当云函数被小程序端调用时,定义的代码会被放在 Node.js 运行环境中执行。我们可以如在 Node.js 环境中使用 JavaScript 一样在云函数中进行网络请求等操作,而且我们还可以通过云函数后端 SDK 搭配使用多种服务
wx.showLoading
显示加载中图标
wx.showLoading({
title: '数据加载中...',
mask: true
})
wx.hideLoading();
创建云函数
右键 cloudfunctions 根目录,点击新建 Node.js 云函数即可新建一个云函数文件夹。
在页面中调用云函数:
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
wx.cloud.callFunction({
name: "getData",
data: {
num: 3
}
}).then(res => {
console.log(res);
});
}
callFunction 函数可以调用云函数,参数以JSON格式传入,name 对应值为函数名,data 对应JSON格式数据为传入参数,返回值在 res 中。
getData 云函数内容如下:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
}) // 使用当前云环境
const db = cloud.database();
// 云函数入口函数
exports.main = async (event, context) => {
// var num=event.num;
return await db.collection('demolist').limit(event.num).get();
}
正确部署上线云函数,需要先用 npm 命令安装依赖,然后进行部署上线。
基于云函数的下拉刷新功能
前端 js 文件:
Page({
/**
* 页面的初始数据
*/
data: {
dataList: []
},
getData(num = 5, page = 0) {
wx.cloud.callFunction({
name: "demoGetList",
data: {
num: num,
page: page
}
}).then(res => {
var oldData = this.data.dataList;
var newData = oldData.concat(res.result.data);
this.setData({
dataList: newData
})
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.getData();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
var page = this.data.dataList.length() / 5;
this.getData(5, page);
},
clickRow(res) {
// 1.获取点击的 id 和索引值
// 2.云函数进行更新操作
// 3.前端连后端,将数据传给后端,后端再返回数据
// 4.重新渲染列表数据
wx.showLoading({
title: '数据加载中...',
mask: true
})
var {
id,
idx
} = res.currentTarget.dataset;
wx.cloud.callFunction({
name:"demoUpList",
data:{
id:id
}
}).then(res=>{
var newData=this.data.dataList[idx];
newData[idx].hits+=7;
this.setData({
dataList:newData
})
})
wx.hideLoading();
}
})
demoGetList 云函数 js 文件内容:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
}) // 使用当前云环境
const db = cloud.database();
// 云函数入口函数
exports.main = async (event, context) => {
var {
num,
page
} = event;
return await db.collection('demolist').skip(page * num).limit(num).get();
}
文件操作
微信支付
Cloud.CloudPay | 微信开放文档 (qq.com)
使用云开发来实现相应的支付功能后,开发者无需关心证书、签名、微信支付服务器端文档,使用简单,代码较少,只需要调用相应的函数即可。
资质
需要是已经开通了微信支付,且已绑定了商户号的小程序。参考接入指引。
注意:对于小程序内提供珠宝玉石、3C数码、盲盒、服饰内衣、海淘、美妆、酒类、家用电器、玩具、箱包皮具、鞋靴、运动户外等商品在线销售及配送服务时,不可使用云开发支付能力,请自行接入微信支付API。交易类小程序运营规范
开通
在云控制台 -> 设置 -> 全局设置中开通。
权限
添加商户号后需要分别进行帐号绑定、jsapi 和 api 退款权限授权。请注意:
1)帐号绑定:商户号的超级管理员需要在微信支付提供的【微信支付商家助手】小程序上确认授权。
2)jsapi 和 api 退款权限,需要前往微信支付商户平台我的授权产品中进行确认授权。说明
完成授权后即可调用微信支付相关接口能力。
JS需要知道的基础知识
事件
什么是事件
- 事件是视图层到逻辑层的通讯方式。
- 事件可以将用户的行为反馈到逻辑层进行处理。
- 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
- 事件对象可以携带额外信息,如 id, dataset, touches。
使用方式
- 在组件中绑定一个事件处理函数。如bindtap,用户点击时会调用相应事件处理函数。
<button type="primary" bindtap="getData">点击获取数据</button>
- 在相应的Page定义中写上相应的事件处理函数,参数是event。
Page({ getData () { db.collection('demolist'). get({ success: res => { console.log(res); this.setData({ dataObj: res.data }); } }); // 拿表 } })
- type代表事件类型
<button type="primary" bindtap="getData">点击获取数据</button>
- bindinput 实时获取输入
Page({ data: { focus: false, inputValue: '' }, bindKeyInput: function (e) { this.setData({ inputValue: e.detail.value }) } })
<view class="page-section"> <view class="weui-cells__title">实时获取输入值:{{inputValue}}</view> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell weui-cell_input"> <input class="weui-input" maxlength="10" bindinput="bindKeyInput" placeholder="输入同步到view中"/> </view> </view> </view>
- bindsubmit 当点击 form 表单中 form-type 为 submit 的 button 组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key
<form bindsubmit="btnSub"> <button type="primary" form-type="submit">提交</button> <button type="primary" form-type="reset">重置</button> </form>
列表渲染
wx:for
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index ,数组当前项的变量名默认为 item 。
<view wx:for="{{dataObj}}">
{{item.title}} - {{item.author}} - {{item.time}}
</view>
<view wx:for="{{dataObj}}">
{{item.title}} - {{item.author}} - {{item.time}}
</view>