uni-app 云函数开发
章节介绍
随着云计算技术的发展,云函数(Serverless)已经成为现代应用开发的重要组成部分。uni-app 提供了基于 uniCloud 的云函数开发能力,开发者可以通过云函数实现服务端逻辑,无需关心服务器的搭建和维护。本章节将详细介绍如何开发 uni-app 云函数,包括云函数的概念、创建方法、调用方式、部署流程和最佳实践,帮助开发者快速上手云函数开发。
核心知识点讲解
1. 云函数概述
云函数是一种无服务器计算服务,允许开发者在云端运行代码,无需关心服务器的搭建、配置和维护。在 uni-app 中,云函数基于 uniCloud 实现,具有以下特点:
- 按需运行:云函数只在被调用时运行,无需长期占用服务器资源
- 自动扩缩容:根据请求量自动调整计算资源,应对高并发场景
- 无需运维:无需关心服务器的搭建、配置、监控和维护
- 低成本:按实际使用量计费,避免资源浪费
- 易于集成:与 uni-app 客户端无缝集成,调用简单
2. 云函数开发环境搭建
在开始云函数开发之前,需要做好以下准备工作:
2.1 注册并登录 DCloud 开发者账号
- 访问 DCloud 开发者中心
- 注册或登录开发者账号
- 实名认证(部分功能需要)
2.2 开通 uniCloud 服务
- 在 DCloud 开发者中心中开通 uniCloud 服务
- 创建服务空间(选择阿里云或腾讯云)
- 记录服务空间 ID,用于后续配置
2.3 配置 HBuilderX
- 下载并安装最新版本的 HBuilderX
- 在 HBuilderX 中登录 DCloud 开发者账号
- 关联服务空间:点击 "uniCloud" > "关联服务空间或项目",选择已创建的服务空间
3. 云函数的创建与配置
3.1 创建云函数
在 HBuilderX 中创建云函数的步骤如下:
在项目中创建云函数目录
- 在 uni-app 项目中,点击 "uniCloud" > "初始化云开发环境"
- 选择服务空间,生成
cloudfunctions目录
创建云函数
- 在
cloudfunctions目录上右键,选择 "新建云函数" - 输入云函数名称,如 "hello"
- HBuilderX 会自动生成云函数的基本结构
- 在
3.2 云函数的基本结构
一个标准的云函数包含以下文件:
cloudfunctions/
└── hello/ # 云函数名称
├── index.js # 云函数入口文件
└── package.json # 云函数配置文件index.js 文件是云函数的入口文件,示例代码如下:
'use strict';
exports.main = async (event, context) => {
// event 包含客户端传递的参数
console.log('event:', event);
// context 包含运行上下文信息
console.log('context:', context);
// 返回结果给客户端
return {
code: 0,
message: 'success',
data: {
hello: 'world'
}
};
};package.json 文件是云函数的配置文件,示例代码如下:
{
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT"
}4. 云函数的调用
在 uni-app 客户端中,调用云函数的方法如下:
4.1 调用云函数
uniCloud.callFunction({
name: 'hello', // 云函数名称
data: { // 传递给云函数的参数
username: 'test',
age: 18
},
success: (res) => {
console.log('调用成功:', res.result);
},
fail: (err) => {
console.error('调用失败:', err);
}
});4.2 使用 async/await 调用
async function callHello() {
try {
const res = await uniCloud.callFunction({
name: 'hello',
data: {
username: 'test',
age: 18
}
});
console.log('调用成功:', res.result);
return res.result;
} catch (err) {
console.error('调用失败:', err);
throw err;
}
}
// 调用函数
callHello();5. 云函数的部署
5.1 本地调试
在部署云函数之前,可以先在本地进行调试:
- 配置本地调试环境
- 点击 "uniCloud" > "云函数本地调试"
- 选择要调试的云函数
- 输入测试参数
- 点击 "运行" 按钮查看执行结果
5.2 部署云函数
部署云函数到云端的步骤如下:
单个云函数部署
- 在云函数目录上右键,选择 "上传部署"
- 等待部署完成
批量部署
- 在
cloudfunctions目录上右键,选择 "上传所有云函数" - 等待所有云函数部署完成
- 在
部署状态查看
- 部署完成后,HBuilderX 会显示部署结果
- 可以在 DCloud 开发者中心的云函数管理页面查看已部署的云函数
6. 云函数的高级特性
6.1 云函数的依赖管理
云函数可以使用 npm 包来扩展功能,步骤如下:
在云函数目录中初始化 npm
- 在云函数目录上右键,选择 "在终端中打开"
- 执行
npm init -y命令初始化 package.json
安装依赖包
- 执行
npm install 包名命令安装依赖,如npm install axios
- 执行
在云函数中使用依赖
- 在 index.js 文件中引入依赖,如
const axios = require('axios');
- 在 index.js 文件中引入依赖,如
部署时自动打包依赖
- 部署云函数时,HBuilderX 会自动打包依赖包
- 确保依赖包的大小不超过云函数的限制(阿里云 50MB,腾讯云 100MB)
6.2 云函数的超时设置
云函数的执行时间有限制,默认超时时间为 3 秒(阿里云)或 10 秒(腾讯云)。可以在 package.json 文件中设置超时时间:
{
"name": "hello",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"cloudfunction-config": {
"timeout": 10 // 超时时间,单位秒
}
}6.3 云函数的环境变量
可以在 DCloud 开发者中心的云函数管理页面设置环境变量,用于存储敏感信息:
- 登录 DCloud 开发者中心
- 进入服务空间管理
- 点击 "云函数" > "配置" > "环境变量"
- 添加环境变量,如数据库连接字符串、API 密钥等
在云函数中使用环境变量:
'use strict';
exports.main = async (event, context) => {
// 获取环境变量
const apiKey = process.env.API_KEY;
console.log('apiKey:', apiKey);
// 使用环境变量
// ...
return {
code: 0,
message: 'success'
};
};7. 云函数与数据库操作
uniCloud 提供了内置的数据库服务,云函数可以直接操作数据库:
7.1 初始化数据库
在云函数中初始化数据库:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
// 数据库操作
// ...
};7.2 数据库操作示例
插入数据:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
const { name, age } = event;
// 插入数据
const res = await db.collection('users').add({
name,
age,
createTime: new Date()
});
return {
code: 0,
message: 'success',
data: res
};
};查询数据:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
const { age } = event;
// 查询数据
const res = await db.collection('users')
.where({ age: { $gt: age } })
.limit(10)
.get();
return {
code: 0,
message: 'success',
data: res.data
};
};更新数据:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
const { id, name } = event;
// 更新数据
const res = await db.collection('users')
.doc(id)
.update({
name,
updateTime: new Date()
});
return {
code: 0,
message: 'success',
data: res
};
};删除数据:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
const { id } = event;
// 删除数据
const res = await db.collection('users')
.doc(id)
.remove();
return {
code: 0,
message: 'success',
data: res
};
};8. 云函数的最佳实践
8.1 代码结构优化
- 模块化:将复杂的云函数拆分为多个模块,提高代码可维护性
- 错误处理:使用 try-catch 捕获错误,返回统一的错误格式
- 日志记录:使用 console.log 记录关键操作,便于调试和排查问题
- 参数校验:对客户端传递的参数进行校验,确保数据安全
8.2 性能优化
减少冷启动时间:
- 减少依赖包的大小
- 避免在初始化时执行耗时操作
- 使用云函数的预留实例(付费功能)
优化数据库操作:
- 使用索引加速查询
- 避免全表扫描
- 批量操作数据,减少数据库请求次数
合理设置超时时间:
- 根据云函数的实际执行时间设置合理的超时时间
- 避免设置过长的超时时间,导致资源浪费
8.3 安全最佳实践
- 使用环境变量:将敏感信息存储在环境变量中,避免硬编码
- 参数校验:对客户端传递的参数进行严格校验,防止注入攻击
- 权限控制:使用 uniCloud 的权限控制功能,限制云函数的调用权限
- 数据加密:对敏感数据进行加密存储和传输
- 防止滥用:限制云函数的调用频率,防止恶意调用
实用案例分析
案例一:开发一个用户注册登录系统
需求分析
开发一个用户注册登录系统,包括用户注册、登录、获取用户信息等功能。
实现步骤
- 创建云函数
创建以下云函数:
register:用户注册login:用户登录getUserInfo:获取用户信息
- 实现用户注册云函数
register/index.js:
'use strict';
const db = uniCloud.database();
const crypto = require('crypto');
exports.main = async (event, context) => {
const { username, password, email } = event;
// 参数校验
if (!username || !password || !email) {
return {
code: 400,
message: '参数错误,缺少必要字段'
};
}
// 检查用户名是否已存在
const userRes = await db.collection('users')
.where({ username })
.get();
if (userRes.data.length > 0) {
return {
code: 400,
message: '用户名已存在'
};
}
// 检查邮箱是否已存在
const emailRes = await db.collection('users')
.where({ email })
.get();
if (emailRes.data.length > 0) {
return {
code: 400,
message: '邮箱已存在'
};
}
// 密码加密
const hashPassword = crypto.createHash('md5').update(password).digest('hex');
// 插入用户数据
const res = await db.collection('users').add({
username,
password: hashPassword,
email,
createTime: new Date(),
lastLoginTime: null
});
return {
code: 0,
message: '注册成功',
data: {
userId: res.id
}
};
};- 实现用户登录云函数
login/index.js:
'use strict';
const db = uniCloud.database();
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
exports.main = async (event, context) => {
const { username, password } = event;
// 参数校验
if (!username || !password) {
return {
code: 400,
message: '参数错误,缺少用户名或密码'
};
}
// 查找用户
const userRes = await db.collection('users')
.where({ username })
.get();
if (userRes.data.length === 0) {
return {
code: 401,
message: '用户名或密码错误'
};
}
const user = userRes.data[0];
// 验证密码
const hashPassword = crypto.createHash('md5').update(password).digest('hex');
if (user.password !== hashPassword) {
return {
code: 401,
message: '用户名或密码错误'
};
}
// 生成 token
const token = jwt.sign(
{ userId: user._id, username: user.username },
'your-secret-key', // 实际使用时应存储在环境变量中
{ expiresIn: '7d' }
);
// 更新最后登录时间
await db.collection('users')
.doc(user._id)
.update({
lastLoginTime: new Date()
});
return {
code: 0,
message: '登录成功',
data: {
token,
user: {
_id: user._id,
username: user.username,
email: user.email
}
}
};
};- 实现获取用户信息云函数
getUserInfo/index.js:
'use strict';
const db = uniCloud.database();
const jwt = require('jsonwebtoken');
exports.main = async (event, context) => {
const { token } = event;
// 参数校验
if (!token) {
return {
code: 401,
message: '未提供 token'
};
}
try {
// 验证 token
const decoded = jwt.verify(token, 'your-secret-key'); // 实际使用时应存储在环境变量中
const userId = decoded.userId;
// 获取用户信息
const userRes = await db.collection('users')
.doc(userId)
.field({
password: false // 不返回密码字段
})
.get();
if (userRes.data.length === 0) {
return {
code: 404,
message: '用户不存在'
};
}
return {
code: 0,
message: '获取成功',
data: {
user: userRes.data[0]
}
};
} catch (error) {
return {
code: 401,
message: 'token 无效或已过期'
};
}
};- 在客户端调用云函数
注册:
async function register() {
try {
const res = await uniCloud.callFunction({
name: 'register',
data: {
username: 'test',
password: '123456',
email: 'test@example.com'
}
});
if (res.result.code === 0) {
console.log('注册成功:', res.result.data);
// 处理注册成功后的逻辑
} else {
console.error('注册失败:', res.result.message);
// 处理注册失败后的逻辑
}
} catch (error) {
console.error('调用云函数失败:', error);
}
}登录:
async function login() {
try {
const res = await uniCloud.callFunction({
name: 'login',
data: {
username: 'test',
password: '123456'
}
});
if (res.result.code === 0) {
console.log('登录成功:', res.result.data);
// 保存 token
uni.setStorageSync('token', res.result.data.token);
// 处理登录成功后的逻辑
} else {
console.error('登录失败:', res.result.message);
// 处理登录失败后的逻辑
}
} catch (error) {
console.error('调用云函数失败:', error);
}
}获取用户信息:
async function getUserInfo() {
try {
const token = uni.getStorageSync('token');
const res = await uniCloud.callFunction({
name: 'getUserInfo',
data: {
token
}
});
if (res.result.code === 0) {
console.log('获取用户信息成功:', res.result.data.user);
// 处理获取用户信息成功后的逻辑
} else {
console.error('获取用户信息失败:', res.result.message);
// 处理获取用户信息失败后的逻辑
}
} catch (error) {
console.error('调用云函数失败:', error);
}
}案例二:开发一个文件上传系统
需求分析
开发一个文件上传系统,包括文件上传、获取文件列表、删除文件等功能。
实现步骤
- 创建云函数
创建以下云函数:
uploadFile:文件上传(使用 uniCloud 的文件存储功能)getFileList:获取文件列表deleteFile:删除文件
- 实现文件上传功能
注意:文件上传通常使用 uniCloud 的客户端上传 API,而不是云函数。这里我们使用云函数来处理上传后的逻辑,如记录文件信息到数据库。
客户端上传代码:
async function uploadFile() {
try {
// 选择文件
const [chooseResult] = await uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera']
});
// 上传文件到 uniCloud
const uploadResult = await uniCloud.uploadFile({
filePath: chooseResult.tempFilePaths[0],
cloudPath: 'images/' + Date.now() + '.jpg'
});
if (uploadResult.success) {
console.log('文件上传成功:', uploadResult.fileID);
// 调用云函数记录文件信息
const recordResult = await uniCloud.callFunction({
name: 'recordFile',
data: {
fileID: uploadResult.fileID,
fileName: chooseResult.tempFiles[0].name,
fileSize: chooseResult.tempFiles[0].size,
fileType: chooseResult.tempFiles[0].type
}
});
if (recordResult.result.code === 0) {
console.log('文件记录成功:', recordResult.result.data);
} else {
console.error('文件记录失败:', recordResult.result.message);
}
} else {
console.error('文件上传失败:', uploadResult.errMsg);
}
} catch (error) {
console.error('上传失败:', error);
}
}recordFile/index.js:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
const { fileID, fileName, fileSize, fileType } = event;
// 参数校验
if (!fileID) {
return {
code: 400,
message: '缺少 fileID 参数'
};
}
// 记录文件信息
const res = await db.collection('files').add({
fileID,
fileName,
fileSize,
fileType,
uploadTime: new Date()
});
return {
code: 0,
message: '记录成功',
data: {
id: res.id
}
};
};- 实现获取文件列表云函数
getFileList/index.js:
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
const { page = 1, pageSize = 10 } = event;
// 计算偏移量
const offset = (page - 1) * pageSize;
// 获取文件列表
const res = await db.collection('files')
.orderBy('uploadTime', 'desc')
.skip(offset)
.limit(pageSize)
.get();
// 获取总记录数
const countRes = await db.collection('files').count();
return {
code: 0,
message: '获取成功',
data: {
files: res.data,
total: countRes.total,
page,
pageSize
}
};
};- 实现删除文件云函数
deleteFile/index.js:
'use strict';
const db = uniCloud.database();
const cloud = uniCloud;
exports.main = async (event, context) => {
const { id, fileID } = event;
// 参数校验
if (!id || !fileID) {
return {
code: 400,
message: '缺少必要参数'
};
}
try {
// 从 uniCloud 存储中删除文件
await cloud.deleteFile({
fileList: [fileID]
});
// 从数据库中删除文件记录
await db.collection('files').doc(id).remove();
return {
code: 0,
message: '删除成功'
};
} catch (error) {
console.error('删除文件失败:', error);
return {
code: 500,
message: '删除失败'
};
}
};- 在客户端调用云函数
获取文件列表:
async function getFileList() {
try {
const res = await uniCloud.callFunction({
name: 'getFileList',
data: {
page: 1,
pageSize: 10
}
});
if (res.result.code === 0) {
console.log('获取文件列表成功:', res.result.data);
// 处理获取文件列表成功后的逻辑
} else {
console.error('获取文件列表失败:', res.result.message);
// 处理获取文件列表失败后的逻辑
}
} catch (error) {
console.error('调用云函数失败:', error);
}
}删除文件:
async function deleteFile(id, fileID) {
try {
const res = await uniCloud.callFunction({
name: 'deleteFile',
data: {
id,
fileID
}
});
if (res.result.code === 0) {
console.log('删除文件成功');
// 处理删除文件成功后的逻辑
} else {
console.error('删除文件失败:', res.result.message);
// 处理删除文件失败后的逻辑
}
} catch (error) {
console.error('调用云函数失败:', error);
}
}学习目标
通过本章节的学习,您应该能够:
- 了解云函数的概念和特点
- 掌握云函数的创建和配置方法
- 学会在客户端调用云函数
- 掌握云函数的部署流程
- 了解云函数的依赖管理
- 掌握云函数操作数据库的方法
- 了解云函数的高级特性和最佳实践
- 能够开发实际的云函数应用,如用户系统和文件上传系统
总结与展望
云函数是 uni-app 开发中的重要组成部分,它为开发者提供了一种简单、高效的服务端开发方式,无需关心服务器的搭建和维护。通过本章节的学习,您已经掌握了云函数的创建、配置、调用、部署和最佳实践,为您开发服务端逻辑奠定了基础。
在后续的学习中,我们将继续探索 uni-app 的高级特性,包括云函数与前端的结合使用、云函数的性能优化、云函数的安全防护等内容,帮助您成为一名更加全面的 uni-app 开发者。
记住,云函数是一种强大的工具,但也需要合理使用。在开发过程中,要注意代码的质量和性能,遵循最佳实践,确保云函数的稳定运行和安全可靠。