Node-RED:Function 节点与变量使用范围笔记

一、Function 节点的代码运行环境

Node-RED是一个物联网可视化编程工具,用于事件驱动应用程序的低代码编程。

Node-RED 的 Function 节点运行在 Node.js 的沙盒环境中,它不是一个完整的 JavaScript 文件,而是 Node-RED 在运行时将你的代码嵌入到一个安全沙盒中执行。

Node-RED 的函数( Function )节点用于编写 JavaScript 代码,对传入的数据进行处理,然后返回多条消息,以使流程继续执行。传入的消息一般以对象形式表示,其名称为msg,通常都会包含一个msg.payload属性,用来保存消息的主体内容。

Function 节点中涉及的变量大致可分为三类:

  1. 局部变量(Function 内部变量)
  2. 消息变量(msg)
  3. 上下文变量(flow / global / env)

参考:https://nodered.com.cn/docs/user-guide/


二、局部变量(Function 内部)

1. 定义方式

在 Function 节点内部使用以下方式定义局部变量:

let a = 10;
const b = "hello";
var c = true;     // 可用,但不推荐

2. 三种变量的区别

定义方式 作用域 是否可重复赋值 是否提升(Hoist) 是否推荐
let 块级作用域 ✔ 是 ❌ 不提升 ⭐ 强烈推荐
const 块级作用域 ❌ 否(引用不可变) ❌ 不提升 ⭐ 推荐(常量)
var 函数级作用域 ✔ 是 ✔ 会变量提升 ❌ 不推荐

示例:

let count = 0;      // 最常用
const PI = 3.14;    // 常量

3. 特点

  • 每次 Function 节点执行时重新创建
  • 执行结束后销毁
  • 不会保存状态(要保存状态必须用 flow/global)
  • 只在当前 Function 内有效

4. 使用场景

  • 一次性计算、临时变量
  • 对 msg 数据做加工处理
  • 内部函数调用时的参数变量

三、消息变量:msg

1. 传递方式

Node-RED 的所有节点都是处理 msg 对象:

msg.payload = 123;
return msg;

2. 特点

  • 通过节点链路连续传递
  • 任意节点都可以读写 msg 的字段
  • 整条链路结束后 msg 数据消失
  • 典型用途:流程间传递数据、状态、标识

3. 修改 msg 的方式

msg.payload = msg.payload + 1;
msg.topic = "sensor";
msg.time = Date.now();

4. 典型使用场景

  • 传递传感器数据
  • 提供给 UI、MQTT、HTTP 等后续节点
  • 控制逻辑的条件判断值

四、flow 变量(当前流程范围共享)

1. 设置和读取

flow.set("counter", 1);
let count = flow.get("counter");

2. 特点

  • 一个 Tab(Flow 页)内所有节点可共享
  • 默认不持久化(可配置 persistent storage)
  • 类似“流程级全局变量”

3. 使用场景

  • 滚动计数器
  • 状态机
  • 中间缓存数据

五、global 变量(全局共享)

1. 使用方法

global.set("token", "ABCD");
let t = global.get("token");

2. 特点

  • 所有流程的所有节点都能访问
  • 默认同样不持久化
  • 用于系统级状态或配置

3. 应用示例

  • API 密钥
  • 登录状态
  • 全系统共享的缓存

六、env 变量(环境变量)

1. 获取方式

let port = env.get("API_PORT");

2. 特点

  • 可在 Node-RED 菜单中配置
  • 只能读取,不能写入
  • 常用作配置项(端口、路径、设备 ID)

七、Function 节点本身的函数能力

Function 节点 == 一个带输入输出的“微型 JS 函数”。

1. 输入参数:msg

Function 节点收到的每一条消息会自动作为 msg 对象传入:

  • msg.payload —— 主体内容
  • 你可以添加、修改、删除 msg 中的任意属性

Function 的输入永远是 msg

let val = msg.payload;

2. 函数返回值return(输出行为)

Function 节点最终要 决定是否输出消息,以及输出什么消息

✅(1)返回 单个 消息对象

return msg;

✅(2)返回 多个 消息(用于多路输出)

return [msg1, msg2];

✅(3)返回数组嵌套(对应多个输出端口)

return [
    [msgA, msgB],  // 第 1 个输出端口
    null,          // 第 2 个输出端口无输出
    [msgC]         // 第 3 个输出端口
];

❌(4)返回 null 或不返回 —— 停止消息继续向下流动阻断输出

return null;
// 或者 simply 不写 return

这样可以让消息 “被吞掉”,常用于:

  • 条件过滤
  • 错误数据阻断
  • 控制流程不继续执行

3. 内部自定义函数(局部函数)

可以在 Function 内声明自己的函数:

function add(a, b){
    return a + b;
}

msg.sum = add(2, 3);
return msg;

也可以使用箭头函数:

const double = x => x * 2;
msg.payload = double(msg.payload);
return msg;

4. 错误处理(内置 node 对象)

报错并停止流程

node.error("无效数据", msg);
return null;

使用 try/catch

try {
    msg.json = JSON.parse(msg.payload);
} catch(e){
    node.error("JSON 解析失败: " + e.message, msg);
    return null;
}
return msg;

5. 调试输出(node.warn / node.log)

node.warn("调试信息");
node.log("普通日志");

八、典型综合示例(函数 + 局部变量 + flow/global/msg)

// 局部变量
let raw = msg.payload;

// 自定义函数
const convert = v => v * 2;

// 使用 flow 作为计数器
let count = flow.get("count") || 0;
count++;
flow.set("count", count);

// 使用 global 存储系统时间
let start = global.get("sysStartTime");

// 环境变量
let port = env.get("API_PORT");

// 输出结果
msg.result = convert(raw);
msg.count = count;
msg.start = start;
msg.port = port;

return msg;

九、常见错误与注意事项

1. ❌ 将状态存在局部变量(无效)

let count = 0;   // 每次执行都会重置

应使用:

let count = flow.get("count") || 0;

2. ❌ 忘记 return msg

msg.payload = 10;
// 没有 return → 后续节点收不到数据

必须:

return msg;

3. flow/global 默认不持久化

如果希望 Node-RED 重启后仍保留数据,需要在 settings.js 中配置:

contextStorage: {
    default: {
        module: "localfilesystem"
    }
}

十、变量作用范围总结表

类型 作用范围 生命周期 可跨节点 是否可持久化 用途
局部变量 当前 Function 内 每次执行重建 临时计算
msg 节点链路中传递 链路结束即消失 节点间传输数据
flow 当前 Tab 所有节点 Node-RED 运行期间 ✔(可配置) 状态机 / 缓存
global 全流程所有节点 Node-RED 运行期间 ✔(可配置) 全局配置
env 环境级 Node-RED 运行期间 配置项