如何编写云函数
函数入参详解
每个云函数调用都会收到两个重要对象: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
1. 创建入口文件 entry.mjs
创建一个名为 entry.mjs
的文件,作为 ES Module 入口点:
// entry.mjs
import { foo } from './util.js';
// 入口函数
export const entry = (event, context) => {
return foo(event, context);
};
2. 在 index.js 中引入入口文件
// index.js
exports.main = async (event, context) => {
const { entry } = await import('./entry.mjs');
return entry(event, context);
};
3. 创建业务模块
// util.js
export const foo = async (event, context) => {
console.log('使用 ES Module 语法');
return {
success: true,
message: 'ES Module 调用成功',
data: event,
};
};
改造现有云函数支持 ES Module
对于现有的云函数代码想要适配 ES Module 规范,本着对代码最小侵入性以及改造成本最低原则:
1. 创建入口文件 entry.mjs
// entry.mjs
// 假设src文件夹是现有的业务代码
import { businessLogic } from './src/index.js';
// 入口函数
export const entry = (event, context) => {
return businessLogic(event, context);
};
2. 创建 package.json 文件
在业务代码目录下创建 package.json
文件:
{
"type": "module"
}
这将告诉 Node.js 将此目录视为 ES Module,并允许使用 ESM 语法导入和导出模块。
3. 修改现有业务代码
将现有的 CommonJS 语法改为 ES Module 语法:
// src/index.js - 改造前(CommonJS)
const helper = require('./helper');
module.exports = {
businessLogic: async (event, context) => {
return helper.process(event);
},
};
// src/index.js - 改造后(ES Module)
import { process } from './helper.js';
export const businessLogic = async (event, context) => {
return process(event);
};
错误处理与日志记录
错误处理实践
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;
};