如何编写云函数
函数入参详解
每个云函数调用都会收到两个重要对象:event
和 context
。
event 对象
event
对象包含触发云函数的事件数据,其内容根据触发方式不同而变化:
- 小程序调用:包含小程序端传入的参数
- HTTP 请求调用:包含 HTTP 请求信息(如请求头、请求体等)
- 定时触发:包含定时触发的相关信息
context 对象
context
对象提供调用上下文信息,帮助您了解函数的运行环境和调用方式:
- 请求 ID:当前调用的唯一标识符
- 调用来源:触发函数的服务或客户端信息
- 执行环境:函数的运行时信息
- 用户身份:调用方的身份信息(如有)
基础代码示例
以下是一个简单的 Node.js 云函数示例,展示如何处理入参并返回结果:
// index.js - 云函数入口文件
exports.main = async (event, context) => {
// 1. 解析云函数入参
const { a, b } = event;
// 2. 执行业务逻辑
const sum = a + b;
// 3. 返回结果
return {
sum,
timestamp: Date.now(),
requestId: context.requestId,
};
};
异步处理实践
由于实例的管理由平台自动处理,推荐云函数采用 async/await 模式,避免使用 Promise 链式调用:
exports.main = async (event, context) => {
// ❌ 不推荐:Promise 链式调用
getList().then((res) => {
// do something...
});
// ✅ 推荐:使用 async/await
const res = await getList();
// do something...
};
环境变量使用
云函数可以通过 process.env
获取环境变量,这是管理配置信息的实践:
获取环境变量
exports.main = async (event, context) => {
// 获取环境变量
const dbUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;
const nodeEnv = process.env.NODE_ENV || 'development';
// 使用环境变量进行配置
const config = {
database: dbUrl,
apiKey: apiKey,
debug: nodeEnv === 'development',
};
return {
message: '环境变量获取成功',
environment: nodeEnv,
};
};
环境变量实践
exports.main = async (event, context) => {
// 检查必需的环境变量
const requiredEnvVars = ['DATABASE_URL', 'API_KEY'];
const missingVars = requiredEnvVars.filter((varName) => !process.env[varName]);
if (missingVars.length > 0) {
throw new Error(`缺少必需的环境变量: ${missingVars.join(', ')}`);
}
// 安全地使用环境变量
const config = {
dbUrl: process.env.DATABASE_URL,
apiKey: process.env.API_KEY,
timeout: parseInt(process.env.TIMEOUT) || 5000,
};
return { success: true, config };
};
注意
- 敏感信息(如 API 密钥、数据库连接字符串)应通过环境变量传递,不要硬编码在代码中
- 环境变量值始终是字符串类型,需要时请进行类型转换
- 建议为环境变量设置默认值,提高代码的健壮性
时区设置
云函数的运行环境内保持的是 UTC 时间,即 0 时区时间,和北京时间有 8 小时的时间差。
可以通过语言的时间处理相关库或代码包(如 moment-timezone),识别 UTC 时间并转换为+8 区北京时间。
时区处理示例
const moment = require('moment-timezone'); // 需在 package.json 中指定并安装依赖
exports.main = async (event, context) => {
// javascript date
console.log(new Date()); // 2021-03-16T08:04:07.441Z (UTC+0)
console.log(moment().tz('Asia/Shanghai').format()); // 2021-03-16T16:04:07+08:00 (UTC+8)
// 获取当前北京时间
const beijingTime = moment().tz('Asia/Shanghai');
return {
utcTime: new Date().toISOString(),
beijingTime: beijingTime.format(),
timestamp: beijingTime.valueOf(),
};
};
时区处理实践
const moment = require('moment-timezone');
exports.main = async (event, context) => {
// 统一时区处理函数
const getBeijingTime = (date = new Date()) => {
return moment(date).tz('Asia/Shanghai');
};
// 格式化时间输出
const formatTime = (date, format = 'YYYY-MM-DD HH:mm:ss') => {
return getBeijingTime(date).format(format);
};
// 业务逻辑中使用
const currentTime = getBeijingTime();
const formattedTime = formatTime();
console.log('当前北京时间:', formattedTime);
return {
success: true,
currentTime: formattedTime,
timestamp: currentTime.valueOf(),
};
};
使用 ES Module 规范
在云函数 Node.js 环境中无法直接采用 ES Module 规范编写代码,主要原因在于,云函数默认支持的入口文件(index.js
)必须遵循 CommonJS 规范,并且文件名必须为 「index.js」。然而,Node.js 对于符合 ES Module 规范的模块文件要求其扩展名为 .mjs
。
在云函数中使用 ES Module 需要创建三个核心文件,形成完整的调用链路:index.js
→ entry.mjs
→ util.mjs
项目结构
cloud-function/
├── index.js # 云函数入口文件(CommonJS)
├── entry.mjs # ES Module 入口文件
└── src
└── util.mjs # 业务逻辑模块,命名可自定义
1. 创建云函数入口文件 index.js
// index.js - 云函数入口文件
exports.main = async (event, context) => {
try {
// 动态导入 ES Module 入口文件
const { entry } = await import('./entry.mjs');
return await entry(event, context);
} catch (error) {
console.error('云函数执行失败:', error);
return {
success: false,
error: error.message,
requestId: context.request_id
};
}
};
2. 创建 ES Module 入口文件 entry.mjs
// entry.mjs - ES Module 入口文件
import { getUserList } from './src/util.mjs';
/**
* ES Module 入口函数
* @param {Object} event - 事件对象
* @param {Object} context - 上下文对象
* @returns {Promise<Object>} 处理结果
*/
export const entry = async (event, context) => {
return getUserList(event, context);
};
3. 创建业务逻辑模块 util.mjs
// src/util.mjs - 业务逻辑模块
import cloudbase from '@cloudbase/node-sdk';
const app = cloudbase.init({
env: 'your-envid', // 替换为您的环境 ID
});
const models = app.models;
export const getUserList = async (event) => {
const res = await models.user.list({});
return {
success: true,
data: res,
};
};
💡 注意: ES Module 文件必须使用
.mjs
扩展名,这样 Node.js 才能正确识别并处理 ES Module 语法。
错误处理与日志记录
错误处理实践
exports.main = async (event, context) => {
try {
// 参数验证
if (!event.userId) {
throw new Error('缺少必需参数: userId');
}
// 业务逻辑处理
const result = await processUserData(event.userId);
return {
success: true,
data: result,
};
} catch (error) {
// 记录错误日志
console.error('函数执行失败:', {
error: error.message,
stack: error.stack,
event,
requestId: context.requestId,
});
// 返回友好的错误信息
return {
success: false,
error: error.message,
requestId: context.requestId,
};
}
};
性能优化建议
执行时间优化
exports.main = async (event, context) => {
const startTime = Date.now();
try {
// 使用并行处理提升性能
const promises = event.items.map((item) => processItem(item));
const results = await Promise.all(promises);
const duration = Date.now() - startTime;
console.log(`函数执行耗时: ${duration}ms`);
return {
success: true,
data: results,
duration,
};
} catch (error) {
console.error('执行错误:', error);
throw error;
}
};
内存使用优化
exports.main = async (event, context) => {
// 分批处理大数据,避免内存溢出
const batchSize = parseInt(process.env.BATCH_SIZE) || 100;
const results = [];
for (let i = 0; i < event.data.length; i += batchSize) {
const batch = event.data.slice(i, i + batchSize);
const batchResult = await processBatch(batch);
results.push(...batchResult);
// 及时清理不需要的变量
batch.length = 0;
// 记录处理进度
console.log(`已处理 ${Math.min(i + batchSize, event.data.length)}/${event.data.length} 条数据`);
}
return results;
};