You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Node-RED中实现网页内容及节点内容的自动动态更新?

Node-RED节点内容动态更新实现方案

问题分析

当前流程里,Template节点渲染的{{payload.content.time}}和{{payload.content.variable}}是页面加载时的静态值。虽然前端用setInterval每秒执行更新函数,但函数里的这两个值不会自动从后端获取新数据,只会重复显示页面初始化时的固定值,所以必须手动刷新页面才能更新。

解决方案一:前端AJAX定时请求后端接口(改动小,适配现有流程)

1. 新增实时数据接口

在现有流程中添加一组节点,专门返回最新生成的数值:

  • HTTP In节点:设置URL为/testcase/data,请求方法选GET
  • Function节点(命名为「获取实时数据」):编写代码从flow存储读取最新值并返回:
var state = {};
state["content"] = {
    "time": new Date().getSeconds(),
    "variable": flow.get("variable") || 0
};
msg.payload = state;
return msg;
  • 连接HTTP In → Function → HTTP Response节点

2. 修改Template节点的前端代码

更新Template中的JavaScript部分,用fetch定时请求新接口获取实时数据:

<script>
    function updateDynamicContent() {
        var dynamicContent = document.getElementById('window');
        var dynamicContent2 = document.getElementById('window2');
        var dynamicContent3 = document.getElementById('window3');

        // 更新本地时间
        var currentTime = new Date();
        dynamicContent.innerHTML = currentTime.getSeconds();

        // 请求后端实时数据
        fetch('/testcase/data')
            .then(response => response.json())
            .then(data => {
                dynamicContent2.innerHTML = data.content.time;
                dynamicContent3.innerHTML = data.content.variable;
            })
            .catch(error => console.error('获取数据失败:', error));
    }

    // 每秒执行一次更新
    setInterval(updateDynamicContent, 1000);
</script>

3. 优化原Function节点逻辑

确保原「Generate content」节点能持续更新flow存储的数值,并且无论请求来自HTTP还是inject都返回消息:

var date = new Date();
var inject = msg.payload;

// 生成数值并存入flow存储
flow.set("variable", Math.round(10*Math.sin(date.getSeconds())));

var state = {};
state["content"] = {
    "time": date.getSeconds(),
    "variable": flow.get("variable"),
    "inject": inject
};
msg.payload = state;
return msg;

完整流程代码

[{"id":"d67de8615ea3d3a0","type":"tab","label":"Flow 2","disabled":false,"info":"","env":[]},{"id":"9777c31bb0ed4db4","type":"http response","z":"d67de8615ea3d3a0","name":"","statusCode":"200","headers":{},"x":760,"y":160,"wires":[]},{"id":"ad9334b7892cf503","type":"function","z":"d67de8615ea3d3a0","name":"Generate content","func":"var date = new Date();\nvar inject = msg.payload;\n\n// generate some value and save it in the flow storage\nflow.set(\"variable\", Math.round(10*Math.sin(date.getSeconds())));\n\n// create some JSON \nvar state = {};\nstate[\"content\"] = {\n    \"time\": date.getSeconds(),\n    \"variable\": flow.get(\"variable\"),\n    \"inject\": inject\n};\n// handle it to a message payload\nmsg.payload = state;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":160,"wires":[["652219a15a6b71e9"]]},{"id":"652219a15a6b71e9","type":"template","z":"d67de8615ea3d3a0","name":"HTML","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <style>\n        /* Center the window both vertically and horizontally */\n        body {\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            height: 100vh;\n            margin: 0;\n            background-color: lightgrey; /* Background color of the page */\n        }\n\n        /* Style for the window */\n        #window {\n            width: 200px;\n            height: 100px;\n            background-color: lightblue; /* Blue color for the window */\n            border: 5px solid black; /* Black border */\n            border-radius: 10px;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            font-size: 36px;\n            font-weight: bold;\n            color: red; /* Red color for the text */\n        }\n        #window2 {\n            width: 200px;\n            height: 100px;\n            background-color: lightblue; /* Blue color for the window */\n            border: 5px solid black; /* Black border */\n            border-radius: 10px;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            font-size: 36px;\n            font-weight: bold;\n            color: red; /* Red color for the text */\n        }\n        #window3 {\n            width: 200px;\n            height: 100px;\n            background-color: lightblue; /* Blue color for the window */\n            border: 5px solid black; /* Black border */\n            border-radius: 10px;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            font-size: 36px;\n            font-weight: bold;\n            color: red; /* Red color for the text */\n        }\n    </style>\n</head>\n<body>\n    <div id=\"window\">\n        <span>var1</span> <!-- The var1 inside the window -->\n    </div>\n    <div id=\"window2\">\n        <span>var2</span> <!-- The var2 inside the window -->\n    </div>\n    <div id=\"window3\">\n        <span>var3</span> <!-- The var3 inside the window -->\n    </div>\n</body>\n<script>\n    function updateDynamicContent() {\n        var dynamicContent = document.getElementById('window');\n        var dynamicContent2 = document.getElementById('window2');\n        var dynamicContent3 = document.getElementById('window3');\n\n        var currentTime = new Date();\n                \n        dynamicContent.innerHTML = currentTime.getSeconds();\n        \n        // 请求后端实时数据\n        fetch('/testcase/data')\n            .then(response => response.json())\n            .then(data => {\n                dynamicContent2.innerHTML = data.content.time;\n                dynamicContent3.innerHTML = data.content.variable;\n            })\n            .catch(err => console.log('数据获取失败:', err));\n\t}\n\n\t// Use setInterval to call the updateDynamicContent function every second\n\tsetInterval(updateDynamicContent, 1000);\n</script>\n</html>\n\n","output":"str","x":570,"y":160,"wires":[["9777c31bb0ed4db4"]]},{"id":"098d5034ca668638","type":"inject","z":"d67de8615ea3d3a0","name":"","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":"1","topic":"","payload":"true","payloadType":"bool","x":130,"y":240,"wires":[["ad9334b7892cf503"]]},{"id":"0a6681231f307f16","type":"http in","z":"d67de8615ea3d3a0","name":"","url":"/testcase","method":"get","upload":false,"swaggerDoc":"","x":150,"y":160,"wires":[["ad9334b7892cf503"]]},{"id":"a1b2c3d4e5f6g7h8","type":"http in","z":"d67de8615ea3d3a0","name":"","url":"/testcase/data","method":"get","upload":false,"swaggerDoc":"","x":150,"y":320,"wires":[["b9c8d7e6f5a4b3c2"]]},{"id":"b9c8d7e6f5a4b3c2","type":"function","z":"d67de8615ea3d3a0","name":"Get Real-time Data","func":"var state = {};\nstate[\"content\"] = {\n    \"time\": new Date().getSeconds(),\n    \"variable\": flow.get(\"variable\") || 0\n};\nmsg.payload = state;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":320,"wires":[["9777c31bb0ed4db4"]]}]

解决方案二:WebSocket实时推送(低延迟,主动更新)

如果需要更低延迟的实时更新,可以用Node-RED的WebSocket节点实现后端主动推送:

  1. 添加websocket out节点,设置路径为/ws/testcase
  2. 将原「Generate content」节点的输出连接到websocket out节点,让后端每秒推送最新数值
  3. 修改Template中的前端代码,用WebSocket接收推送数据:
// 建立WebSocket连接
const ws = new WebSocket(`ws://${window.location.host}/ws/testcase`);

ws.onmessage = function(event) {
    const data = JSON.parse(event.data);
    document.getElementById('window2').innerHTML = data.content.time;
    document.getElementById('window3').innerHTML = data.content.variable;
};

// 本地时间仍用定时更新
setInterval(() => {
    document.getElementById('window').innerHTML = new Date().getSeconds();
}, 1000);

这种方式无需前端定时请求,后端数值更新时直接推送给页面,效率更高。

内容的提问来源于stack exchange,提问作者Arseniy Sleptsov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:59:51