如何基于Chart.js实现从MySQL取数的无刷新实时更新折线图
Chart.js 无刷新实时更新实现方案
核心实现逻辑
通过定时异步请求后端接口拉取最新数据库数据,直接修改已初始化的Chart实例属性并触发内置渲染方法,全程无需重载index.php页面。
第一步:拆分独立JSON数据接口
将原来嵌在JS里的数据库查询逻辑独立为纯数据接口文件,比如api/get_chart_data.php,用于输出结构化的图表JSON数据,避免把数据库逻辑硬编码在前端脚本生成流程中:
<?php header("Content-Type: application/json; charset=UTF-8"); // 数据库连接配置 $koneksi = mysqli_connect("数据库用户名", "数据库密码", "数据库名"); if (!$koneksi) { die(json_encode(["error" => "数据库连接失败"])); } // 保留原有查询逻辑:取最近5条记录按时间正序排列,单次查询同时取时间和传感器值 $query = mysqli_query($koneksi, 'SELECT created_at, sensor1 FROM (SELECT * FROM modul1 ORDER BY id DESC LIMIT 5) Var1 ORDER BY id ASC'); $response = [ 'labels' => [], 'values' => [] ]; while ($row = mysqli_fetch_assoc($query)) { $response['labels'][] = $row['created_at']; $response['values'][] = floatval($row['sensor1']); } echo json_encode($response); exit; ?>
第二步:改造图表初始化逻辑
将图表实例保存为全局可访问变量,修正原代码中存在的语法疏漏(原代码animation行无配置值、缺逗号,会导致JS解析失败):
// 全局存储图表实例,供后续更新调用 let myLineChart; async function initChart() { const ctx = document.getElementById("GrafikRTU"); // 首次加载拉取初始数据 const res = await fetch('api/get_chart_data.php'); const initialData = await res.json(); myLineChart = new Chart(ctx, { type: 'line', data: { labels: initialData.labels, datasets: [{ label: "传感器1数值", lineTension: 0.3, backgroundColor: "rgba(78, 115, 223, 0.05)", borderColor: "rgba(78, 115, 223, 1)", pointRadius: 3, pointBackgroundColor: "rgba(78, 115, 223, 1)", pointBorderColor: "rgba(78, 115, 223, 1)", pointHoverRadius: 3, pointHoverBackgroundColor: "rgba(78, 115, 223, 1)", pointHoverBorderColor: "rgba(78, 115, 223, 1)", pointHitRadius: 10, pointBorderWidth: 2, data: initialData.values, }] }, options: { maintainAspectRatio: false, animation: { duration: 300 // 数据更新时的动画时长,避免界面闪烁 }, layout: { padding: { left: 10, right: 25, top: 25, bottom: 0 } }, scales: { xAxes: [{ time: { unit: 'date' }, gridLines: { display: false, drawBorder: false }, ticks: { maxTicksLimit: 7 } }], yAxes: [{ ticks: { maxTicksLimit: 5, padding: 10, // 替换原示例中的美元符号逻辑,适配传感器数值展示 callback: function(value) { return value; } }, gridLines: { color: "rgb(234, 236, 244)", zeroLineColor: "rgb(234, 236, 244)", drawBorder: false, borderDash: [2], zeroLineBorderDash: [2] } }], }, legend: { display: false }, tooltips: { backgroundColor: "rgb(255,255,255)", bodyFontColor: "#858796", titleMarginBottom: 10, titleFontColor: '#6e707e', titleFontSize: 14, borderColor: '#dddfeb', borderWidth: 1, xPadding: 15, yPadding: 15, displayColors: false, intersect: false, mode: 'index', caretPadding: 10 } } }); }
第三步:实现定时数据刷新
通过定时器异步拉取最新数据,直接更新图表实例的数据集属性,调用Chart内置的update()方法完成视图刷新:
async function refreshChartData() { // 图表未初始化完成时跳过本次请求 if (!myLineChart) return; try { const res = await fetch('api/get_chart_data.php'); const newData = await res.json(); // 更新图表的标签与数据 myLineChart.data.labels = newData.labels; myLineChart.data.datasets[0].data = newData.values; // 触发图表重新渲染 myLineChart.update(); } catch (err) { console.warn('数据刷新失败:', err); } } // 页面DOM加载完成后执行初始化 + 开启定时任务 window.addEventListener('DOMContentLoaded', () => { initChart(); // 可根据实际数据采集频率调整间隔,建议不低于3秒,避免增加数据库压力 setInterval(refreshChartData, 5000); });
优化建议
- 不要将数据库账号密码硬编码在前端可见的脚本生成流程中,拆分独立接口后数据库逻辑全部运行在后端,不会被前端直接获取
- 原代码对同一张表执行了两次重复查询取时间和传感器值,可合并为单次查询同时返回两个字段,降低数据库查询开销
- 可增加数据对比判断:只有拉取的最新数据和当前图表数据不一致时,才触发
update()方法,减少不必要的动画渲染 - 定时刷新间隔不要设置过短,需和数据库的实际数据写入频率匹配,避免无效查询浪费服务器资源
内容的提问来源于stack exchange,提问作者Mr AllRounders
相关产品推荐
相关产品推荐

