云函数是运行在云端的 JavaScript
代码,是基于 Node.js
的扩展。每个云函数是一个js包,在云函数被调用时,由serverless调度系统分配硬件资源启动一个node环境来运行这个云函数。
创建云函数
云函数需要在HBuilderX
编辑器中创建,在cloudfuctions
目录上单击鼠标右键选择新建云函数
新建的云函数是一个目录,目录中有index.js
入口文件。一个最简单的云函数只需要在这个入口js
文件里面编写javascript
代码即可。云函数里几乎可以使用所有的nodejs
库。
注意事项:
- 同一个云空间内,云函数的名称不可重复。
- 单个云函数大小限制为10M(包含node_modules)。
- 云函数内使用commonjs规范,不可使用import、export。
云函数运行环境
目前腾讯云和阿里云均支持选择nodejs
版本,有nodejs8
、nodejs12
两个选项,需要在云函数创建时设定,不可修改。需要在云函数的package.json文件的cloudfunction-config->runtime
字段进行配置.
云函数启动后环境会保留一段时间(如15
分钟),超过保留期后若该云函数一直没有被再调用,那这个环境会被释放。所以云函数有冷
/热
启动的概念。
云函数的冷、热启动
云函数是一种按需执行的服务,即云函数在不被触发(请求)的时候,计算资源是不被激活的。
当一个云函数初次被触发时,其完整过程如下:
- 实例化计算实例
- 加载函数代码
- 启动 node
- 执行代码
函数被调用时,执行这些完整步骤的过程一般称作冷启动, 冷启动的耗时长于热启动,一般在1秒出头。
而如果函数实例和执行进程都被复用的情况下一般被定义为热启动,热启动没有性能问题。
如果一个云函数实例长时间没有被再次调用,则该计算实例会被回收;后续再次调用该云函数时,就会再次触发云函数的冷启动。因为存在冷热启动的差异,云函数中的全局变量就可能出现每次不一样的情况,也就是云函数是无状态的。
云函数package.json
默认创建的云函数没有packages.json
文件,一般来说也只有安装依赖或公共模块才需要packages.json。不过,package.json也可以用来配置云函数的一些运行选项,比如内存大小、url化、定时触发等等。
{
"name": "add-article",
"version": "1.0.0",
"description": "新增文章",
"main": "index.js",
"dependencies": {
// 云函数的依赖,包括公共模块及自行安装的npm依赖
},
"extensions": {
// 云函数使用的扩展库
},
"cloudfunction-config": {
"concurrency": 10, // 单个云函数实例最大并发量,不配置的情况下默认是1
"memorySize": 256, // 函数的最大可用内存,单位MB,可选值:128|256|512|1024|2048,默认值256
"timeout": 5, // 函数的超时时间,单位秒,默认值5。最长为60秒,阿里云在定时触发时最长可以是600秒
// triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
"triggers": [{ // 阿里云腾讯云均为此形式,请阅读下方说明
// name: 触发器的名字,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger,name不对阿里云生效
"name": "myTrigger",
// type: 触发器类型,目前仅支持 timer (即 定时触发器),type不对阿里云生效
"type": "timer",
// config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见https://uniapp.dcloud.net.cn/uniCloud/trigger。使用阿里云时会自动忽略最后一位,即代表年份的一位在阿里云不生效
"config": "0 0 2 1 * * *"
}],
// 云函数Url化path部分,阿里云需要以/http/开头
"path": "",
"runtime": "" // nodejs版本,可选Nodejs8、Nodejs12,默认:Nodejs8
}
}
云函数访问云数据库
在云函数中访问云数据库可能是大部分云函数的主要工作,它让我们操作数据库就像操作一个JS对象那么简单。
代码语言:javascript复制//在云函数中获取云数据库示例
const db = uniCloud.database();
//在云函数中获取云数据库users集合的数据
const dbRes = db.collection("users").get();
在后面的小节中我们会更加详细的学习到在云函数中操作云数据库。
uni-app前端调用云函数
在uni-app项目中用如下代码调用一个云函数,并传递两个参数
代码语言:javascript复制uniCloud.callFunction({
name:"云函数名称",
data:{//云函数传参
a:1
b:{
c:2
}
},
success:(res)=>{
}
});
在云函数中我们可以这样获取到传递的参数,并通过return直接返回数据给前端
代码语言:javascript复制// 云函数index.js入口文件代码
'use strict';
exports.main = async (event, context) => {
//event为客户端上传的参数
return {
sum:event.a event.b.c
} // 通过return返回结果给客户端
}
云函数的传入参数有两个,一个是event
对象,一个是context
对象。
event
指的是触发云函数的事件。当客户端调用云函数时,event
就是客户端调用云函数时传入的参数。 注意:event大小不可超过100kbcontext
对象包含了本次请求的上下文,包括客户端的操作系统(os
)、运行平台(platform
)、应用信息(appid
)
'use strict';
exports.main = async (event, context) => {
//event为客户端上传的参数
//...
//context中可获取客户端调用的上下文
let clientIP = context.CLIENTIP // 客户端ip信息
let clientUA = context.CLIENTUA // 客户端user-agent
let spaceInfo = context.SPACEINFO // 当前环境信息 {spaceId:'xxx',provider:'tencent'}
// 以下四个属性只有使用uni-app以callFunction方式调用才能获取
let os = context.OS //客户端操作系统,返回值:android、ios 等
let platform = context.PLATFORM //运行平台,返回值为 mp-weixin、app-plus等。注意:vue3版本uni-app将app-plus修改为了app,此处为保证旧版本兼容性未进行统一,推荐后续在业务中都使用app作为客户端标识
let appid = context.APPID // manifest.json中配置的appid
let deviceId = context.DEVICEID // 客户端标识,新增于HBuilderX 3.1.0,同uni-app客户端getSystemInfo接口获取的deviceId
//... //其它业务代码
}
云函数中访问云函数
在云函数中调用另一个云函数,和uni-app前端调用云函数类似,唯一的不同是云函数中调用云函数不支持callback形式。
代码语言:javascript复制//在云函数中调用云函数
let callFunctionResult = await uniCloud.callFunction({
name: "test",
data: { a: 1 }
})
云函数中请求其他http服务
在云函数中需要请求其他的http
服务,可以直接使用uniCloud.request
const res = await uniCloud.httpclient.request(apiUrl, {
method: 'POST',
data: {
test: 'testValue'
},
contentType: 'json', // 指定以application/json发送data内的数据
dataType: 'json' // 指定返回值为json格式,自动进行parse
})
console.log(res)
/*返回的数据结构
{
"data": {"name": "DCloud"}, // 响应内容
"status": 200, // 状态码
"headers": { // 响应头,仅作示例,不同服务器返回的有差异
"date": "Tue, 29 Dec 2020 08:10:30 GMT",
"content-type": "application/json",
"content-length": "276",
"connection": "keep-alive",
"server": "gunicorn/19.9.0",
"access-control-allow-origin": "*",
"access-control-allow-credentials": "true"
}
}
*/
默认情况下request接口不会处理返回的数据,即不传
dataType
参数时会返回buffer类型的数据,如需自动解析json格式的返回结果,需要将dataType
设置为"json"
前面提到,在云函数中我们几乎可以使用所有的nodejs
库,那么当然也可以使用大家所熟悉的axios
,got
来发起普通的http
请求。
云函数的公共模块
多个云函数中有相同逻辑的代码,应该抽离为公共模块,然后被多个云函数引用。以下面的目录结构为例,介绍一下如何使用。
代码语言:javascript复制cloudfunctions
├─common // 云函数公用模块目录
| └─hello-common // 云函数公用模块
| ├─package.json
| └─index.js // 公用模块代码,可以不使用index.js,修改 package.json 内的 main 字段可以指定此文件名
└─use-common // 使用公用模块的云函数
├─package.json // 在 use-common 目录执行 npm init -y 生成
└─index.js // 云函数入口文件
创建并引入公用模块
- 在
cloudfunctions
目录下创建common
目录(有的话就不用建了) - 在
common
目录单击鼠标右键选择新建公共模块
- 在要使用公共模块的云函数上单击鼠标右键选择管理功能模块依赖
勾选要使用的公共模块即可
注意事项:
- 如果要更新所有依赖某公用模块的云函数,可以在
common
目录下的公共模块目录上单击鼠标右键选择更新依赖本模块的云函数
- 公用模块命名不可与
nodejs
内置模块重名 - 通过命令行安装公共模块时不应该使用yarn
使用公用模块
仍以上面的目录为例,在公用模块内exports
,在云函数内require
即可。示例代码如下:
// common/hello-common/index.js
function getVersion() {
return '0.0.1'
}
module.exports = {
getVersion,
secret: 'your secret'
}
// use-common/index.js
'use strict';
const {
secret,
getVersion
} = require('hello-common')
exports.main = async (event, context) => {
let version = getVersion()
return {
secret,
version
}
}
如果仅需要导出一个function
还可以使用以下写法
// common/hello-common/index.js
module.exports = function(e){
return e
}
// use-common/index.js
'use strict';
const echo = require('hello-common')
exports.main = async (event, context) => {
let eventEcho = echo(event)
return {
eventEcho
}
}
云函数的调试和日志
在云函数内使用console.log
、console.info
、console.warn
、console.error
四种方式打印日志。在开发阶段,我们可以随时切换是连接本地云函数
还是连接云端云函数
。
在uniCloud Web控制台
,我们也可以查看云函数的日志,
注意,uniCloud Web控制台
云函数日志最多只会保留7天。
注意事项
- 临时存储空间
云函数是运行在云端的代码,运行环境由云服务器弹性调配,这是和传统
Node.js
应用很大的区别。换言之,云函数每次执行的宿主环境(可简单理解为虚拟机或服务器硬件)可能相同,也可能不同,因此传统Node.js
开发中将部分信息存储本地硬盘或内存的方案就不再适合,建议通过云数据库或云存储的方案替代。
- 普通云函数的单路由开发模式
一个云服务空间内的云函数数量是有上限的,比如阿里云的云服务空间,云函数数量上限为48个。本课程中只会推荐大家使用阿里云的云服务空间,毕竟它是完全免费的。
- 云函数内的时区
云端的云函数中使用的时区是
UTC 0
,而不是UTC 8
,在云函数中使用时间时需特别注意。云函数在HBuilderX本地运行时,时区则是电脑的时区,很可能是UTC 8
。
小结
本节中的内容来自对uniCloud官方文档的重新梳理,为了让本课程的学习曲线更加平缓,仅保留我认为对本课程有用的部分