基础篇 2:小程序·云开发基础知识
小程序·云开发 是微信团队联合腾讯云团队推出的一套小程序开发解决方案。小程序·云开发为开发者提供完整的云端流程,弱化后端和运维概念,开发者无需购买和管理底层计算资源,包括服务器、数据库、静态存储,只需使用平台提供的简易 API 进行核心业务等开发,实现快速上线和迭代,把握业务发展的黄金时期。
简单来说,小程序开发中用到的服务器、数据库和静态资源管理,都可以托管到「小程序·云开发」上,小程序开发者只需要关注业务功能实现,而不需要关心服务器运维等带来的问题。小程序开发主要用到的是前端技术,后端开发和服务运维对于前端开发者来说有一定的门槛,而小程序·云开发的出现就是解决这个问题的。
一、小程序·云开发特点
- 提供完整后端服务解决方案,包括数据库、静态资源管理和云函数(功能服务)
- 背靠腾讯云大平台,腾讯云丰富的 API 和功能都可以简单调用
- 对于普通开发者,完全免费
- 对于高级服务型小程序,提供更强服务支持、计费弹性、不使用不计费的特点
- 无服务器搭建,无域名配置,直接调用 API 使用
对于我们普通开发者来说,小程序·云开发是个不错的练手平台。下面详细介绍下小程序·云开发提供的功能。
小程序·云开发提供数据库、云函数和静态存储三大功能,还有小程序的用户管理功能,在用户管理界面可以轻松掌握小程序的授权用户情况。目前云开发的这些功能已经深度整合到「微信开发者工具」中,可以在顶部点击「云开发」进入。
二、申请小程序·云开发
1.小程序·云开发的开通
如果还没有开通小程序·云开发账号,首次点击「云开发」会出现下面的界面,该界面主要是云开发的介绍和申请入口。
点击蓝色的「创建资源环境」按钮进入新建环境界面。
第二步出现选择套餐信息,现在公测阶段只有一个免费套餐选项,将来应该会有更多套餐选择。填写上「环境名称」点击确认之后,会使用小程序开发账号在腾讯云开通一个 fake account
。到此小程序·云开发就开通了!
TIPS:目前一个小程序账号可以免费开通两个云开发账号,免费版本的限制应该也要注意:
- 数据库存储空间:2GB
- 文件存储空间:5GB
- 文件存储外网下行流量:5GB/月
- 云函数数量:20
- 云函数资源使用量:10万GBs/月
- API调用次数:3万次/天
这些对于我们做普通的小程序开发练习已经足够了!
2.小程序·云开发在小程序中的调用
小程序·云开发可以在小程序中直接通过调用 wx.cloud.*
的方式进行调用,在调用云开发 API 之前,需要先调用 wx.cloud.init
对云开发进行初始化:
wx.cloud.init({
env: 'tianqi-xxx'
})
初始化时,需要传入 env
参数,该参数为创建小程序·云开发时的环境 ID
,可以在「云开发」页面右上角「当前环境」下拉菜单中找到:
下面详细介绍下小程序·云开发的三大功能。
三、数据库
小程序·云开发的数据库是一种 NoSQL 云端数据库,数据以 JSON 格式存储,在底层支持弹性可扩展、自动容灾、监控管理,所以开发者不需要关注数据库的运维。从提供的文档和接口来看,云开发的数据库应该是由 MongoDB 实现的。
每个数据库是由多个集合(collection,类比关系型数据库中表的概念)组成,集合有多个 JSON 文档(行)组成,NoSQL 的特点是没有固定的字段,所以整个集合可以看成一个大的 JSON 数组,一个集合在数据库中的存储格式如下:
[
{
"_id": 1,
"_openid": "ax123CVadb",
"name": "Alice",
"city": "Guangzhou"
},
{
"_id": 2,
"_openid": "xj372nJdfa",
"name": "Bob",
"city": "Shenzhen"
}
]
需要说明的是,文档中的 _id
是唯一的,开发者可以插入数据的时候自定义,另外 _openid
是增加文档默认创建的,代表当前小程序用户的唯一标示,后面实战部分笔者会重点介绍小程序的用户授权相关的内容。
1.快速入门
详细介绍可以见小程序·云开发的使用文档,这里笔者整理出常用的 API 使用方法:
// 初始化
wx.cloud.init({
env: 'tianqi-xxx'
})
// 获取数据库实例
const db = wx.cloud.database()
// 增
db.collection('集合名称').add({
data: {} // 插入的数据
}).then(res => {
// 可以通过 res._id 获取创建的记录的 id
console.log(res._id)
})
// 删
db.collection('集合名称').doc('文档 ID').remove().then(res => {
console.log('removed')
})
// 改
db.collection('集合名称').doc('文档 ID').update({
data: {
title: '我的第 1 篇文章', // 只更新 title 字段,其他不更新
}
}).then(res => {
// 可以通过 res._id 获取创建的记录的 id
console.log(res._id)
})
// 查
db.collection('集合名称').doc('文档 ID').get().then(res => {
// 打印结果,res.data 即为记录的数据
console.log(res)
})
const _ = db.command // 取指令
db.collection('集合名称').where({
// 查找条件
category: 'computer',
properties: {
memory: _.gt(8), // 表示大于 8
}
})
小程序·云开发的数据库查询命令是可以使用查询筛选指令的,使用查询筛选指令可以缩小查询范围,找到查询条件的文档。
以下指令挂载在 db.command
下
类型 | 接口 | 说明 |
---|---|---|
比较运算 | eq | 字段 == |
neq | 字段 != | |
gt | 字段 > | |
gte | 字段 >= | |
lt | 字段 < | |
lte | 字段 <= | |
in | 字段值在数组里 | |
nin | 字段值不在数组里 | |
逻辑运算 | and | 表示需同时满足指定的所有条件 |
or | 表示需同时满足指定条件中的至少一个 |
举例说明:在
diary
集合中找出openid
某个值并且创建时间(tsModified
)在start
和end
之间的文档。
db
.collection('diary')
.where({
openid,
tsModified: _.gte(start).and(_.lt(end))
})
.get()
2.数据库的索引
增加合适的索引可以提升文档的查找效率,比如根据时间、用户 ID 查找,可以将时间和用户 ID 字段设置为索引项,笔者在使用的时候发现:在小程序·云开发管理后台并不能对某个字段增加唯一索引。
3.自带权限管理
在研发中,经常会针对不同的用户设置不同的数据库权限,例如:某条记录是用户 A 创建的,则只有用户 A 可以删除或者更新,其他用户只有查看的权限,这样的需求很常见,实际开发起来却非常费劲,往往要写不少权限判断的代码,小程序·云开发的数据库支持「权限管理」功能,可以针对这类需求对不同的集合进行统一处理,大大降低开发的门槛!
集合的操作权限包括以下四种:
- 所有用户可读,仅创建者及管理员可写
- 仅创建者及管理员可读写
- 所有用户可读,仅管理员可写
- 仅管理员可读写
可以在「云开发 -> 数据库 -> 选择某个集合 -> 权限设置」页面进行设置。
四、文件存储
小程序·云开发的文件存储功能是专为存储和提供用户生成的内容(如图片或视频)的开发者打造的。开发者可使用腾讯云的 SDK 来存储图片、音频、视频或其他由用户生成的内容。在小程序内,则可以通过云开发的 API 直接上传、下载和管理存储。
公共使用的静态资源,可以通过「云开发 -> 存储」界面直接上传和管理,上传之后,就可以在界面内找到资源的 CDN 地址。
而对于小程序内需要上传和管理的则通过下面几个 API 来实现:
// 上传,上传后会返回资源的 ID
wx.cloud.uploadFile
// 下载
wx.cloud.downloadFile
// 根据资源 ID 获取资源访问地址
wx.cloud.getTempFileURL
// 根据资源 ID 列表删除某资源
wx.cloud.removeFile
跟数据库权限管理一样,如果需要用户自己上传的内容自己可以管理,那么需要设置存储的操作权限,在「云开发 -> 存储」下可以设置全局的存储操作权限。
五、云函数
云函数是腾讯云提供的一套函数计算解决方案,我们可以将每个功能 API 做成单个可执行的函数,然后放到腾讯云上去托管,每个云函数是相互独立可执行的。代码编写完成后放到云端,不执行不调用不收费,执行调用按照调用次数和 CPU 等计算资源的占用情况收费。有了云函数,开发者无须搭建和购买服务器,只需要将写好的云函数代码上传部署到腾讯云,即可以在小程序内通过 wx.cloud.callFunction
的方法进行调用。
1.创建云函数
创建云函数有两种方式,一种是直接在小程序开发者工具中进行操作:
进入「云开发 -> 云函数 -> 添加」创建:
另外一种是直接在小程序开发者工具中上传,在上传之前需要配置小程序的 project.config.json
,指明哪个路径为云函数目录:
{
"cloudfunctionRoot": "./cloud-functions/"
}
配置完成后,在小程序开发者工具的编辑器中展开 cloud-functions
文件夹,选择对应的云函数文件夹,右键选择「上传并部署」即可:
2.写个简单云函数
小程序·云开发的云函数是执行在 Node.js 8.9.0 版本下的,云函数必须在 index.js 有 main
方法为入口,例如下面的代码:
// 命名为 test 的函数内容 index.js
exports.main = async (event, context) => {
let {a, b} = event
return new Promise((resolve, reject) => {
resolve({result: parseInt(a) + parseInt(b)})
})
}
除了上面 Promise 的写法,还可以用回调的方式:
// 命名为 test 的函数内容 index.js
// 回调方式 callback
exports.main = async (event, context, callback) => {
let {a, b} = event
callback(null, {result: parseInt(a) + parseInt(b)})
}
TIPS:
- 云函数如果不存在
main
的方法,上传部署的时候会报错!- 云函数回调方式写法遵循「错误优先」原则(Error-First Callback)。
云函数接受两个 JSON 格式的参数 event
和 context
,两者分别代表:
event
:平台将event
入参传递给执行方法,通过此event
入参对象,代码将与触发函数的事件(event)交互,event
可以获取wx.cloud.callFunction
调用的参数data
context
:平台将context
入参传递给执行方法,通过此context
入参对象,代码将能了解到运行环境及当前请求的相关内容
TIPS: 开发者可以在云函数内获取到每次调用的上下文(appId、openId 等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openId),这俩值可以从
event.userInfo
中读取。
上面 test
的函数上传到腾讯云之后,我们在小程序的 JS 代码中可以使用下面的方法进行调用:
wx.cloud.callFunction({
name: 'test',
data: {
a: 1,
b: 2
}
}).then(r=>{
// 因为 main 的方法实际是个 promisify 的返回,所以可以直接使用 then/catch
console.log(r)
}).catch(e=>{
console.log(e)
})
3.云函数的依赖管理
在云函数中,可以像正常的 Node.js 一样,使用 package.json
和 node_modules
来对依赖进行管理,在开发完代码之后,需要将 node_modules
文件夹一起上传到云端去。下面笔者将带着大家做一个什么值得买的简单抓取的云函数,讲解函数编写、npm 模块使用、云函数本地测试整个流程。
先说下,笔者要实现的功能:
- 根据传入的分类参数,获取什么值得买对应分类的最新文章内容
- 提取出文章列表的 title、image、mall,即文章名称、文章的配图和优惠所属的网站
首先在 cloud-functions 文件夹(该文件夹是我们创建的云函数文件夹),创建一个 smzdm
的云函数,目录结构如下:
cloud-functions
├── smzdm
│ ├── index.js
然后进入 smzdm
目录,执行 npm init
,按照提示填写内容以后,会在该目录下生成 package.json
文件,接下来需要安装抓取「什么值得买」手机站点的两个 npm 模块:
安装命令如下:
# 进入 smzdm 目录,执行
npm install --save request cheerio
安装之后的目录结构如下:
cloud-functions
├── smzdm
│ ├── node_modules
│ ├── index.js
│ └── package.json
然后我们开始编写 index.js 内容,第一步引入模块,编写 main
方法:
// 引入 requst 和 cheerio
const request = require('request')
const cheerio = require('cheerio')
exports.main = async (event = {}) => {
// 获取具体什么值得买网站分类
let category = event.category || 'diannaoshuma'
return new Promise((resolve, reject) => {
})
}
第二步,开始编写具体的逻辑,即使用 request.get
先获取 HTML 内容,然后使用 cheerio
将 HTML 内容进行结构化,经过使用 Chrome 查看器查看,发现最新文章都包含在一个 class
为 card-group-list
的 div 下,然后找到 zm-card-title
等每个文章的标题、图片和商城信息,将结果放到一个数组,最后 resolve
输出:
const request = require('request')
const cheerio = require('cheerio')
exports.main = async (event = {}) => {
let category = event.category || 'diannaoshuma'
return new Promise((resolve, reject) => {
request.get(`https://m.smzdm.com/fenlei/${category}/`, (e, req, body) => {
if (!e && req.statusCode === 200) {
const $ = cheerio.load(body)
const result = []
$('.card-group-list').each((i, v) => {
let $v = $(v)
let title = $v.find('.zm-card-title').text().trim()
let image = $v.find('.zm-card-media img').attr('src')
let mall = $v.find('.card-mall').text().trim()
result.push({
title,
image,
mall
})
})
resolve(result)
}
})
})
}
这样就完成了一个带有依赖模块的云函数编写,上传到腾讯云部署之后,在小程序中使用:
wx.cloud.callFunction({
name: 'smzdm',
data: {
category: 'diannaoshuma'
}
}).then(r=>{console.log(r)})
4.云函数的调试
云函数有一个很不方便的地方,就是测试起来相对来说比较麻烦,我们不能每次都上传到云端,通过 wx.cloud.callFunction
的方式进行调用,下面介绍几种测试的方法。
(1)线下:函数自己使用 Node 来测试
这种方法就是在 index.js 的最后,增加一个测试方法,比如:
const request = require('request')
const cheerio = require('cheerio')
const main = (exports.main = async (event = {}) => {
let category = event.category || 'diannaoshuma'
return new Promise((resolve, reject) => {
request.get(`https://m.smzdm.com/fenlei/${category}/`, (e, req, body) => {
if (!e && req.statusCode === 200) {
const $ = cheerio.load(body)
const result = []
$('.card-group-list').each((i, v) => {
let $v = $(v)
let title = $v.find('.zm-card-title').text().trim()
let image = $v.find('.zm-card-media img').attr('src')
let label = $v.find('.card-label').text().trim()
let mall = $v.find('.card-mall').text().trim()
result.push({
title,
image,
label,
mall
})
})
resolve(result)
}
})
})
})
main({category: 'diannaoshuma'}).then(r=>{console.log(r)})
然后使用 Node.js 直接运行该文件:node index.js
(2)线上:使用开发者工具
在小程序开发者工具的云开发控制台内有测试的工具,进入路径为:「云开发 -> 云函数列表 -> 点击具体云函数 -> 右上角测试」。
对于测试的参数,还可以保存下来模板,方便以后使用。
5.云函数的 mock server
上文提到的测试方法,都是将小程序的研发流程完全割裂开来,不能完整地测试小程序的代码,只能要么测试云函数要么测试小程序,那么测试小程序的代码就需要上线云函数,实际还是一种效率不高的做法。这里笔者介绍一种本地 mock server 的方式来开启云函数的测试,这种方式可以打通小程序开发的整个流程。具体做法分两步:
- 将云函数作为一个接受请求参数的 server 来访问
- 使用
wx.request
构造请求拿到云函数的处理结果,然后跑通整个研发流程
笔者在 mock server 选型上,选择使用 Express 自建 mock server 的方式。
首先,启动一个 Express server:
const express = require('express')
const app = express()
app.listen(3000, () => {
console.log(`开发服务器启动成功:http://127.0.0.1:3000`)
})
nodemon index.js
然后将云函数引入进程序中来,作为一个路由 handler 来接受http url 的参数,处理请求之后,将返回的处理结果通过 Express 的 res.json
输出。
const test = require('./cloud-functions/test/').main
app.get('/api/test', (req, res, next) => {
// 将 req.query 传入
test(req.query).then(res.json.bind(res)).catch((e) => {
console.error(e)
next(e)
})
// next()
})
这样访问 http://127.0.0.1:3000/api/test?a=1&b=2
就会输出结果了。到这里笔者就将 mock server 搭建完毕了。
六、小结
本节重点介绍了腾讯新推出的小程序云开发的基础知识,云开发由 NoSQL 数据库、文件存储和云函数三个云产品组成,其中数据库和文件存储都可以单独设置用户权限,这样极大地降低了 UGC(用户产生内容)类小程序的开发门槛。
云函数是小程序·云开发中一个很重要的产品,本节介绍了云函数的基本用法和注意事项,同时针对小程序云函数的开发测试比较困难的情况,提出了 mock server 的解决方式。