Node-RED:自定义节点开发:打造专属工具箱 - 详解

头图

Node-RED:自定义节点开发:打造专属工具箱


关键字:Node-RED自定义节点节点开发npm包HTML模板封装调试

摘要

去年我们做了一个智慧楼宇项目,有 20 多种设备需要接入 Modbus。
每次都要写一堆 Function 节点:解析寄存器、转浮点、处理字节序……
不仅流程图臃肿,还容易出错。

后来,我花了一天时间,封装了一个 modbus-parser 自定义节点

  • 左侧调色板拖出来就能用
  • 可视化配置寄存器地址和数据类型
  • 输出标准化 JSON

从此,新增设备只需填参数,不再写一行 JS。

今天这篇文章,就带你从零开始,开发属于你自己的 Node-RED 节点
你将学会:

  • 自定义节点的完整目录结构
  • 如何用 HTML 定义配置界面
  • 如何用 JavaScript 实现核心逻辑
  • 如何本地测试与调试
  • 如何发布到 npm 供团队或社区使用

这不是 API 文档复读,而是一份 “从想法到可复用积木”的实战开发手册


一、为什么需要自定义节点?——超越子流程的边界

子流程(Subflow)虽好,但有局限:

  • ❌ 无法发布到 npm 共享
  • ❌ 配置界面简陋(只有文本框)
  • ❌ 不能包含 Dashboard 控件
  • ❌ 无法实现复杂状态管理

而自定义节点:

  • ✅ 拥有专业级配置面板
  • ✅ 支持图标、下拉菜单、校验提示
  • ✅ 可打包成 npm 包,一键安装
  • ✅ 性能更高(原生节点调度)

适用场景:

  • 封装通用协议(如 Modbus、BACnet)
  • 集成特定 SDK(如阿里云 IoT、微信 API)
  • 构建团队内部标准组件库

二、项目结构:一个标准自定义节点长什么样?

Node-RED 对自定义节点有约定目录结构:

node-red-contrib-my-node/
├── package.json                # npm 包信息
├── README.md                   # 使用说明
├── icon.png (可选)             # 节点图标
├── nodes/
│   ├── my-node.html            # 配置界面(前端)
│   └── my-node.js              # 运行逻辑(后端)
└── test/ (可选)
    └── my-node_spec.js         # 单元测试

关键命名规则:

  • 文件名必须一致:my-node.html + my-node.js
  • npm 包名建议以 node-red-contrib- 开头(社区惯例)

三、第一步:创建 package.json

{
"name": "node-red-contrib-modbus-parser",
"version": "1.0.0",
"description": "Parse Modbus register data into structured JSON",
"keywords": ["node-red", "modbus", "parser"],
"node-red": {
"nodes": {
"modbus-parser": "nodes/modbus-parser.js"
}
},
"dependencies": {
"buffer": "^6.0.3"
}
}

⚠️ 注意:node-red.nodes 字段告诉 Node-RED 哪个 JS 文件对应哪个节点名。


四、定义配置界面:my-node.html

这是用户双击节点时看到的弹窗。

基础模板:

<script type="text/html" data-template-name="modbus-parser">
  <div class="form-row">
    <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
      <input type="text" id="node-input-name" placeholder="My Parser">
        </div>
          <div class="form-row">
            <label><i class="fa fa-cog"></i> Register Address</label>
              <input type="number" id="node-input-register" min="0" value="0">
                </div>
                  <div class="form-row">
                    <label>Data Type</label>
                      <select id="node-input-type">
                        <option value="uint16">Unsigned 16-bit</option>
                          <option value="float32">Float 32-bit</option>
                            <option value="string">String</option>
                              </select>
                                </div>
                                </script>
                                  <script type="text/javascript">
                                  RED.nodes.registerType('modbus-parser', {
                                  category: 'function',       // 分类:function, input, output 等
                                  color: '#a6bbcf',           // 节点颜色
                                  defaults: {
                                  name: {value: ""},
                                  register: {value: 0, required: true},
                                  type: {value: "uint16"}
                                  },
                                  inputs: 1,
                                  outputs: 1,
                                  icon: "arrow-in.png",       // 内置图标或自定义 icon.png
                                  label: function() {
                                  return this.name || "Modbus Parser";
                                  }
                                  });
                                </script>

高级技巧:

  • 表单校验required: true 自动提示
  • 动态选项:用 JavaScript 动态填充 <select>
  • 帮助链接:在底部加 <p>See <a href="...">docs</a></p>

五、实现核心逻辑:my-node.js

这是节点运行时的后端代码。

基础框架:

module.exports = function(RED) {
function ModbusParserNode(config) {
RED.nodes.createNode(this, config);
const node = this;
// 保存配置
node.register = config.register;
node.type = config.type;
// 监听输入消息
node.on('input', function(msg, send, done) {
try {
// 核心解析逻辑
let value = parseModbusData(
msg.payload,
node.register,
node.type
);
// 修改 msg 并发送
msg.parsed = value;
send(msg);
done(); // 标记完成(用于异步)
} catch (e) {
node.error("解析失败: " + e.message, msg);
done(e); // 报错并终止
}
});
}
RED.nodes.registerType("modbus-parser", ModbusParserNode);
};
function parseModbusData(buffer, reg, type) {
// 实现具体解析...
if (type === "float32") {
return buffer.readFloatBE(reg * 2);
}
// ...
}

关键概念:

  • send():发送输出消息(支持多输出:send([msg1, null])
  • done():标记异步操作完成(Node-RED 1.0+ 必须调用)
  • node.error():触发 Catch 节点捕获

六、本地开发与调试:高效迭代的秘诀

步骤 1:链接到本地 Node-RED

# 在自定义节点目录下
npm link
# 进入 Node-RED 用户目录(如 ~/.node-red)
npm link node-red-contrib-modbus-parser

步骤 2:启用自动重载(开发模式)

编辑 settings.js

export NODE_RED_ENABLE_SAFE_MODE=false
// 或启动时加 --safe-mode=false

修改 .js.html 后,刷新浏览器即可生效,无需重启!

步骤 3:调试技巧

  • .js 中加 node.warn("Debug: " + JSON.stringify(msg))
  • 用浏览器开发者工具查看 HTML 表单绑定
  • console.log 输出到 Node-RED 终端(慎用)

七、图标与国际化:提升专业感

自定义图标:

  • 在节点目录放 icon.png(推荐 20x20 或 30x30)
  • .htmlregisterType 中指定:icon: "icon.png"

多语言支持(可选):

<script type="text/html" data-help-name="modbus-parser">
  <p>解析 Modbus 寄存器数据。</p>
  </script>

帮助文档会显示在节点右键 → “Help” 中。


八、发布到 npm:让全世界都能用

步骤:

  1. 注册 npm 账号(npm adduser

  2. 确保 package.json 版本号正确

  3. 执行发布:

    npm publish
  4. 安装验证:

    npm install node-red-contrib-modbus-parser

发布前检查清单:

  • README.md 包含使用示例
  • 关键配置有默认值
  • 错误处理完善
  • 无硬编码敏感信息
  • 测试通过

社区贡献:发布后可在 Node-RED Flow 提交,让更多人发现。


九、最佳实践:写出高质量节点

原则说明
单一职责一个节点只做一件事(如“解析”,不包含“连接”)
配置最小化默认值合理,高级选项可折叠
错误明确报错信息包含上下文(如“寄存器 100 超出范围”)
性能注意避免在 input 中做 heavy 计算,考虑缓存
向后兼容升级时保留旧配置字段

十、实战案例:封装阿里云 IoT 上报节点

需求:

  • 输入 msg.payload = { temp: 25 }
  • 自动加上设备证书、时间戳
  • 调用阿里云 MQTT 上报

节点配置界面:

  • ProductKey(文本)
  • DeviceName(文本)
  • DeviceSecret(密码框)
  • Topic(文本,默认 /user/update

核心逻辑(简化):

const aliyun = require('aliyun-iot-device-sdk');
node.device = aliyun({
productKey: config.productKey,
deviceName: config.deviceName,
deviceSecret: config.deviceSecret
});
node.on('input', (msg) => {
node.device.publish(config.topic, JSON.stringify(msg.payload));
});

✅ 效果:团队所有设备上报,统一用此节点,安全又规范。


写在最后:从使用者到创造者

Node-RED 的强大,不仅在于它提供的 4000+ 节点,
更在于每个人都能成为生态的建设者

当你把重复的 50 行 Function 代码,
封装成一个带图标、有校验、可共享的自定义节点时,
你就完成了从“自动化使用者”到“工具创造者”的跃迁。

在此之前,不妨动手做一个小节点:
比如 “timestamp-adder” —— 自动给 msg 加上 ISO 时间戳
当你在调色板看到自己写的节点亮起绿灯时,你会明白——
真正的自由,是能按自己的方式扩展世界


博客签名2021
posted on 2026-01-31 20:18  ljbguanli  阅读(0)  评论(0)    收藏  举报