Skip to main content

如何编写云函数

函数入参详解

每个云函数调用都会收到两个重要对象:eventcontext

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;
};