云函数
云函数即在云端(服务器端)运行的函数
。在物理设计上,一个云函数可由多个文件组成,占用一定量的 CPU 内存等计算资源;各云函数完全独立;可分别部署在不同的地区。开发者无需购买、搭建服务器,只需编写函数代码并部署到云端即可在小程序端调用,同时云函数之间也可互相调用。
一个云函数的写法与一个在本地定义的 JavaScript 方法无异,代码运行在云端 Node.js 中
。当云函数被小程序端调用时,定义的代码会被放在 Node.js 运行环境中执行。我们可以如在 Node.js 环境中使用 JavaScript 一样在云函数中进行网络请求等操作,而且我们还可以通过云函数后端 SDK 搭配使用多种服务,比如使用云函数 SDK 中提供的数据库和存储 API 进行数据库和存储的操作,这部分可参考数据库和存储后端 API 文档。
云开发的云函数的独特优势在于与
微信登录鉴权的无缝整合
。当小程序端调用云函数时,云函数的传入参数中会被注入小程序端用户的 openid,开发者无需校验 openid 的正确性因为微信已经完成了这部分鉴权,开发者可以直接使用该 openid
。
设置云函数根目录
在项目根目录找到 project.config.json 文件,新增 cloudfunctionRoot 字段,指定本地已存在的目录作为云开发的本地根目录
由于我有两套环境,所有配置好之后没有需要选择环境, 在mycloud文件夹上右键选择环境
然后右键新建node.js云函数testClound1
会自动生成一个例子
我们再创建的云函数文件夹上右键可以看到三个选项
分别是本地调试、创建并部署所有文件(包括node_modules),以及创建并部署文件(云端安装依赖);
我们先本地调试好之后,再部署。
先进入testClound1文件夹安装依赖
我们新建一个页面,调用云函数
wx.cloud.callFunction({
// 云函数名称
name: 'testClound1',
// 传给云函数的参数
data: {
},
success: function(res) {
console.log('success-clound',res) // 3
},
fail: console.error
})
控制台打印
调用成功,并且返回了调用者的openid.
现在来讲解一些云函数
- 在云函数中使用 wx-server-sdk
云函数属于管理端,在云函数中运行的代码拥有不受限的数据库读写权限和云文件读写权限
云函数中使用 wx-server-sdk 需在对应云函数目录下安装 wx-server-sdk 依赖,在创建云函数时会在云函数目录下默认新建一个 package.json 并提示用户是否立即本地安装依赖。请注意云函数的运行环境是 Node.js,因此在本地安装依赖时务必保证已安装 Node.js,同时 node 和 npm 都在环境变量中。如不本地安装依赖,可以用命令行在该目录下运行:
npm install --save wx-server-sdk@latest
在云函数中调用其他 API 前,同小程序端一样,也需要执行一次初始化方法:
const cloud = require('wx-server-sdk')
// 给定字符串环境 ID:接下来的 API 调用都将请求到环境 some-env-id
cloud.init({
env: 'some-env-id'
})
或者
const cloud = require('wx-server-sdk')
// 给定 DYNAMIC_CURRENT_ENV 常量:接下来的 API 调用都将请求到与该云函数当前所在环境相同的环境
// 请安装 wx-server-sdk v1.1.0 或以上以使用该常量
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
现在云函数升级之后,直接在云函数根目录右键就可以选择环境。 init也不用传递参数选择环境。
然后是云函数的入口函数
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
云函数的传入参数有两个,一个是event
对象,一个是 context
对象。event 指的是触发云函数的事件,当小程序端调用云函数时,event 就是小程序端调用云函数时传入的参数,外加后端自动注入的小程序用户的 openid 和小程序的 appid
。context 对象包含了此处调用的调用信息和运行状态
,可以用它来了解服务运行的情况。
比如我们在小程序端传递参数
wx.cloud.callFunction({
// 云函数名称
name: 'testClound1',
// 传给云函数的参数
data: {
num1: 2,
num2: 3
},
success: function(res) {
console.log('success-clound',res) // 3
},
fail: console.error
})
云函数打印event对象
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
console.log('event', event)
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
右键testCound1开启本地调试
打开云函数本地调试控制台
在云函数本地调试控制台看到接受到了小程序端传递的参数
我们可以在云函数中返回给小程序端
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
console.log('event', event)
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
num: event.num1+event.num2
}
}
小程序端在调用云函数成功之后返回了结果
当然我们也可以使用then的语法获取云函数的结果
wx.cloud.callFunction({
// 云函数名称
name: 'testClound1',
// 传给云函数的参数
data: {
num1: 2,
num2: 3
}
}).then(res=>{
console.log('云函数调用成功', res);
}).catch(err =>{
console.log('云函数调用失败', err);
})
获取用户信息
云开发的云函数的独特优势在于与微信登录鉴权的无缝整合
。当小程序端调用云函数时,云函数的传入参数中会被注入小程序端用户的 openid,开发者无需校验 openid 的正确性,因为微信已经完成了这部分鉴权,开发者可以直接使用该 openid。与 openid 一起同时注入云函数的还有小程序的 appid。
从小程序端调用云函数时,开发者可以在云函数内使用 wx-server-sdk
提供的 getWXContext
方法获取到每次调用的上下文(appid
、openid
等),无需维护复杂的鉴权机制,即可获取天然可信任的用户登录态(openid
)
const cloud = require('wx-server-sdk')
exports.main = async (event, context) => {
// 这里获取到的 openId、 appId 和 unionId 是可信的,
// 注意 unionId 仅在满足 unionId 获取条件时返回
let { OPENID, APPID, UNIONID } = cloud.getWXContext()
return {
OPENID,
APPID,
UNIONID,
}
}
异步返回结果
经常,我们需要在云函数中处理一些异步操作,在异步操作完成后再返回结果给到调用方。此时我们可以通过在云函数中返回一个 Promise
的方法来完成
// index.js
exports.main = async (event, context) => {
return new Promise((resolve, reject) => {
// 在 3 秒后返回结果给调用方(小程序 / 其他云函数)
setTimeout(() => {
resolve(event.num1 + event.num2)
}, 3000)
})
}
这次我们上传并部署云函数 (node_modules不上传) (注意,每次有修改都需要重新上传和部署)
点击左上角云开发 去云函数控制台
每次上传后要看函数状态变为已部署 才能测试(所有更多推荐本地云开发测试)
刷新小程序
云函数中调用其他云函数
假设我们要在云函数中调用另一个云函数 sum 并返回 sum 所返回的结果:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
return await cloud.callFunction({
name: 'sum',
data: {
x: 1,
y: 2,
}
})
}
灰度和版本
云开发提供发布版本(快照)和多版本间调整流量比例的能力。借此能力用户可以完成灰度,同时可以灰度函数配置
云函数版本
一个云函数可以发布多个版本,一个版本就是一个函数在当前时刻的快照,包含其代码和配置(超时时间、环境变量等)。
云函数始终存在一个 LATEST 版,即最新版
。编辑器中上传云函数和在控制台更改配置始终更改 LATEST 版本。
在发布版本时,总是从 LATEST 当前的状态发布一个版本(快照)。
要进行灰度,只需更改版本之前的流量配比,即可实现灰度。
流量比例
在没有设置流量比例前,默认情况下都是 100% 流量导向 LATEST
版本,在发布一个或多个版本后,即可调整各个版本之间的流量比例。
比如现在要进行灰度,首先我们发布版本 1
,然后设置 100% 流量到版本 1
,接着更改 LATEST
代码,此时希望 10% 的线上流量给到需要灰度观察的最新代码,则设置 10% 流量给到 LATEST
,90% 流量给到 1
。
日志服务
开发者可通过微信云开发提供的日志服务实现日志采集和检索分析等功能,方便开发者通过日志快速的发现和定位问题。每条日志可最长存储30天,超过 30 天的日志将被自动清理。
开发者可前往微信开发者工具云控制台的云函数高级日志界面开启高级日志服务
日志采集
可使用 wx-server-sdk
提供的 logger
方法打日志:
- 通过
logger()
方法取得log
对象 - 调用
log
对象上的log
/info
/warn
/error
(对应不同 level 的日志等级)方法,传入一个对象作为参数,每调用一次会产生一条日志记录 - 对象的每一个
<key, value>
对都会成为日志一条记录中的一个可检索的键值对,其中value
不论值是什么都会被转成字符串
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
const log = cloud.logger()
log.info({
name: 'xx',
cost: 10,
attributes: {
width: 100,
height: 200,
},
colors: ['red', 'blue'],
})
// 输出到日志记录中会有这么一条记录:
// {
// "level": "info",
// "name": "xx",
// "cost": "10",
// "attributes": "{ width: 100, height: 200 }",
// "colors": "[ "red", "blue" ]"
// ..., // 其他系统字段
// }
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
定时触发器
如果云函数需要定时 / 定期执行,也就是定时触发,我们可以使用云函数定时触发器。配置了定时触发器的云函数,会在相应时间点被自动触发,函数的返回结果不会返回给调用方。
在需要添加触发器的云函数目录下新建文件 config.json,格式如下:
{
// triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
"triggers": [
{
// name: 触发器的名字,规则见下方说明
"name": "myTrigger",
// type: 触发器类型,目前仅支持 timer (即 定时触发器)
"type": "timer",
// config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见下方说明
"config": "0 0 2 1 * * *"
}
]
}
- 定时触发器名称 (name):最大支持 60 个字符,支持
a-z
,A-Z
,0-9
,-
和_
。必须以字母开头,且一个函数下不支持同名的多个定时触发器。- 定时触发器触发周期 (config):指定的函数触发时间。填写自定义标准的 cron 表达式来决定何时触发函数。有关 cron 表达式的更多信息,请参考下面的内容。
Cron 表达式
Cron 表达式有七个必需字段,按空格分隔
实现增删改查
把微信小程序云开发(一)中的例子拿过来,用云函数实现
创建云函数 list 实现查询
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database() //获取数据库的引用
const userCollection = db.collection('user')
// 云函数入口函数
exports.main = async (event, context) => {
return userCollection
.get() // 查询
.then(res => {
console.log('获取user成功', res);
return {
code: 0,
list: res.data
}
}).catch(err => {
console.log('获取user失败', err);
return {
code: -1,
msg: err
}
})
}
小程序端int方法调用list函数
wx.cloud.callFunction({
name: 'list',
data:{},
}).then(res=>{
console.log('获取列表', res);
if(res.result.code === 0){
this.setData({
list: res.result.list
})
}
}).catch(err=>{
console.log('err', err);
})
return false;
接下来分别创建add ,delete,update云函数实现剩下功能
add
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database() //获取数据库的引用
const userCollection = db.collection('user')
// 云函数入口函数
exports.main = async (event, context) => {
const {name, age, sex} = event
return userCollection
.add({ // 新增
data: {
name,
age: +age,
sex: +sex
}
}).then(res => {
return {
code: 0
}
}).catch(err => {
return {
code: -1,
msg: err
}
})
}
udpate
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database() //获取数据库的引用
const userCollection = db.collection('user')
// 云函数入口函数
exports.main = async (event, context) => {
const {
id,
data
} = event
return userCollection
.doc(id) // 找到需要修改的数据索引
.update({ // 修改数据
data
}).then(res => {
return {
code: 0
}
}).catch(err => {
return {
code: -1,
msg: err
}
})
}
delete
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database() //获取数据库的引用
const userCollection = db.collection('user')
// 云函数入口函数
exports.main = async (event, context) => {
const {
id
} = event
return userCollection
.doc(id)
.remove()
.then(res => {
return {
code: 0
}
}).catch(err => {
return {
code: -1,
msg: err
}
})
}
小程序端代码
cloud.js
const db = wx.cloud.database() //获取数据库的引用
const userCollection = db.collection('user') // 获取集合的引用
Page({
data: {
sex: 1,
name: '',
age: 0,
list: []
},
onLoad() {
this.init()
},
init(){
wx.cloud.callFunction({
name: 'list',
data:{},
}).then(res=>{
console.log('获取列表', res);
if(res.result.code === 0){
this.setData({
list: res.result.list
})
}
}).catch(err=>{
console.log('获取列表失败-err', err);
})
},
// 获取输入框 的姓名和年龄
bindinput(e) {
const type = e.currentTarget.dataset.type
this.setData({
[type]: e.detail.value
})
console.log(e.detail.value);
},
// 获取性别
radioChange(e) {
this.setData({
sex: e.detail.value
})
console.log('radio', e.detail.value);
},
// 新增方法的实现
addFn() {
const {
name,
age,
sex
} = this.data;
if (!name || !age) {
return wx.showToast({
title: '需要输入姓名或年龄',
})
}
wx.cloud.callFunction({
name: 'add',
data:{
name,
age: +age,
sex: +sex
},
}).then(res=>{
console.log('新增成功', res);
if(res.result.code === 0){
this.init()
}
}).catch(err=>{
console.log('新增失败-err', err);
})
},
updateFn(e) {
const id = e.currentTarget.dataset.id
const {name, age, sex} = this.data;
const data = {sex}
if (name) {
data.name = name
}
if (age) {
data.age = age
}
wx.cloud.callFunction({
name: 'update',
data:{
id,
data
},
}).then(res=>{
console.log('更新成功', res);
if(res.result.code === 0){
this.init()
}
}).catch(err=>{
console.log('更新失败-err', err);
})
},
deleteFn(e) {
const id = e.currentTarget.dataset.id
wx.cloud.callFunction({
name: 'delete',
data:{
id
},
}).then(res=>{
console.log('删除成功', res);
if(res.result.code === 0){
this.init()
}
}).catch(err=>{
console.log('删除失败-err', err);
})
},
})
增删改查也实现了。
上传文件 (可以是图片、音频、视频、文件等)
wx.cloud.uploadFile
将本地资源上传至云存储空间,如果上传至同一路径则是覆盖写
新建一个页面 ,创建button按钮 点击 上传本地的图片
upload(){
console.log('点击上传');
wx.cloud.uploadFile({
cloudPath: 'imgs/葫芦娃.jpg',
filePath: '/static/image/葫芦娃.jpg', // 文件路径
success: res => {
// get resource ID
console.log('上传成功',res);
console.log(res.fileID)
},
fail: err => {
console.log('err', err);
// handle error
}
})
},
然后我们去云开发控制台
点击储存
点击图片,右侧查看详情信息
我们可以看到图片的fileId(小程序使用),以及下载地址(链接可以直接浏览器打开)
本地选择图片上传本展示
由于wx.chooseImage无法获取图片名,只能获取图片的本地临时文件路径列表 (本地路径) ,所有上传数据库的图片需要自己命名
pic.wxml
<button bindtap="upload">上传文件</button>
<image src="{{imgSrc}}"></image>
pic.js
Page({
data: {
imgSrc: ''
},
upload() {
const that = this
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success(res) {
console.log('res', res);
// tempFilePath可以作为img标签的src属性显示图片
const tempFilePaths = res.tempFilePaths
that.cloundUpdate(tempFilePaths[0], '测试图片.jpg')
}
})
},
cloundUpdate(filePath, nameStr) {
wx.cloud.uploadFile({
cloudPath: 'imgs/' + nameStr,
filePath: filePath,
success: res => {
// get resource ID
console.log('上传成功', res);
console.log(res.fileID)
this.setData({
imgSrc: res.fileID
})
},
fail: err => {
console.log('err', err);
// handle error
}
})
}
})
视频、音频,文件道理一样,无非就是 api不同而已。
wx.chooseMedia: 拍摄或从手机相册中选择图片或视频
wx.chooseVideo: 拍摄视频或从手机相册中选视频
wx.chooseImage: 从本地相册选择图片或使用相机拍照
wx.chooseMessageFile: 从客户端会话选择文件 (通过type可以上传视频、图片和其他)
文件下载
wx.cloud.downloadFile
download(){
wx.cloud.downloadFile({
fileID: 'cloud://test-0ge6yywxbfeb7294.7465-test-0ge6yywxbfeb7294-1306429729/imgs/葫芦娃.jpg'
}).then(res => {
// get temp file path
console.log('下载文件成功', res);
console.log(res.tempFilePath)
this.setData({
imgSrc: res.tempFilePath
})
}).catch(error => {
// handle error
console.log('下载文件失败', err);
})
},
fileID 就是我们上次的fileID. 也可以到云开发控制台点击图片右侧获取
从云存储空间删除文件,一次最多 50 个
我们去云开发控制台获取测试普通的file ID
delete(){
wx.cloud.deleteFile({
fileList: ['cloud://test-0ge6yywxbfeb7294.7465-test-0ge6yywxbfeb7294-1306429729/imgs/测试图片.jpg']
}).then(res => {
// handle success
console.log(res.fileList)
console.log('删除测试图片成功', res);
}).catch(error => {
// handle error
console.log('删除测试图片失败', err);
})
},
云文件 ID 换取真实链接
用云文件 ID 换取真实链接,公有读的文件获取的链接不会过期,私有的文件获取的链接十分钟有效期。一次最多取 50 个
getUrl(){
wx.cloud.getTempFileURL({
fileList: ['cloud://test-0ge6yywxbfeb7294.7465-test-0ge6yywxbfeb7294-1306429729/imgs/葫芦娃.jpg']
}).then(res => {
// get temp file URL
console.log(res.fileList)
this.setData({
imgSrc: res.fileList[0].tempFileURL
})
}).catch(error => {
// handle error
})
},