目录
效果图
修改前App.vue代码:
<template> <div id="app"> <canvas id="myChart"></canvas> </div> </template> <script> import axios from 'axios'; import { Chart, registerables } from 'chart.js'; import 'chartjs-adapter-luxon'; Chart.register(...registerables); export default { name: 'App', data() { return { chart: null, remainingData: [], speedData: [] }; }, mounted() { this.fetchData(); setInterval(this.fetchData, 60000); // 每分钟更新数据 }, methods: { fetchData() { axios.get('/data.json') .then(response => { console.log('接收数据:', response.data); // 确保转换后的时间戳格式正确,使用 new Date(item.timestamp) 而不是 Date(item.timestamp) this.remainingData = response.data.map(item => { return { x: new Date(item.timestamp), y: item.remaining }; }); this.calculateSpeed(); // 确保remainingData和speedData不为空,防止图例点击时出现undefined错误 if (this.remainingData.length === 0) { this.remainingData = [{ x: new Date(), y: 0 }]; } if (this.speedData.length === 0) { this.speedData = [{ x: new Date(), y: 0 }]; } // 防止无限递归调用,只在数据确实有更新时更新图表 if (!this.chart) { this.updateChart(); } else { // 更新数据并重新渲染 this.chart.data.datasets[0].data = this.remainingData; this.chart.data.datasets[1].data = this.speedData; this.chart.update(); // 只更新已有的图表 } }) .catch(error => { console.log('获取数据异常:', error); }); }, calculateSpeed() { const speeds = []; for (let i = 1; i < this.remainingData.length; i++) { const speed = this.remainingData[i].y - this.remainingData[i - 1].y; speeds.push({ x: this.remainingData[i].x, y: speed }); } this.speedData = speeds; }, updateChart() { this.chart = new Chart( document.getElementById('myChart').getContext('2d'), { type: 'line', data: { datasets: [ { label: 'Remaining', data: this.remainingData, borderColor: 'rgb(75, 192, 192)', tension: 0.1, fill: false }, { label: 'Speed', data: this.speedData, borderColor: 'rgb(255, 99, 132)', tension: 0.1, fill: false } ] }, options: { scales: { x: { type: 'time', // 使用时间轴 time: { unit: 'minute', // 设为 minute 使点不聚集 tooltipFormat: 'yyyy-MM-dd HH:mm', displayFormats: { minute: 'HH:mm' // X 轴时间格式化 } }, ticks: { source: 'auto', autoSkip: true, maxRotation: 0, // 防止时间戳旋转 } }, y: { beginAtZero: true // 保证 Y 轴从 0 开始 } }, plugins: { tooltip: { mode: 'index', intersect: false }, legend: { onClick: (e, legendItem, legend) => { const datasetIndex = legendItem.datasetIndex; const dataset = legend.chart.data.datasets[datasetIndex]; // 确保点击图例时,数据集存在并且被初始化 if (!dataset || !dataset.data || !Array.isArray(dataset.data)) { console.log('数据集不存在或者未初始化'); return; } // 切换数据集的显示状态 dataset.hidden = !dataset.hidden; legend.chart.update(); } } }, responsive: true, maintainAspectRatio: false } } ); } } } </script>
出现 RangeError: Maximum call stack size exceeded
错误,可能是由于 setInterval
直接引用了 fetchData
,而 fetchData
中又更新了图表或页面元素,导致无限递归问题。每次调用 fetchData
可能会触发多次图表更新,最终导致栈溢出。
为了解决这个问题,你可以通过以下方式优化:
解决方案
- 使用箭头函数传递上下文:避免
setInterval
中的上下文丢失,确保this
的正确性。 - 防止重复图表创建:在
fetchData
和updateChart
中,确保图表只在需要的时候更新,避免重复调用造成栈溢出。 - 处理异步调用:确保每次
fetchData
的数据处理逻辑正确,避免多个请求导致的图表过度更新。
修改后的App..vue 代码:
<template> <div id="app"> <canvas id="myChart"></canvas> </div> </template> <script> import axios from 'axios'; import { Chart, registerables } from 'chart.js'; import 'chartjs-adapter-luxon'; Chart.register(...registerables); export default { name: 'App', data() { return { chart: null, remainingData: [], speedData: [], lastFetchedData: null, // 存储上次的数据 intervalId: null // 存储 setInterval 的 ID }; }, mounted() { this.fetchData(); this.intervalId = setInterval(this.fetchData, 60000); // 每分钟更新数据 }, beforeUnmount() { // 清理定时器,防止组件销毁时继续触发 if (this.intervalId) { clearInterval(this.intervalId); } }, methods: { fetchData() { axios.get('/data.json') .then(response => { console.log('接收数据:', response.data); const newData = response.data.map(item => { return { x: new Date(item.timestamp), y: item.remaining }; }); // 判断数据是否真的有变化,只有在变化时才更新图表 if (JSON.stringify(newData) !== JSON.stringify(this.remainingData)) { console.log('数据发生变化,更新图表'); this.remainingData = newData; this.calculateSpeed(); // 确保remainingData和speedData不为空 if (this.remainingData.length === 0) { this.remainingData = [{ x: new Date(), y: 0 }]; } if (this.speedData.length === 0) { this.speedData = [{ x: new Date(), y: 0 }]; } // 防止无限递归调用,只在数据确实有更新时更新图表 if (!this.chart) { this.updateChart(); } else { this.chart.data.datasets[0].data = this.remainingData; this.chart.data.datasets[1].data = this.speedData; this.chart.update(); // 只更新已有的图表 } } else { console.log('数据未变化,不更新图表'); } }) .catch(error => { console.log('获取数据异常:', error); }); }, calculateSpeed() { const speeds = []; for (let i = 1; i < this.remainingData.length; i++) { const speed = this.remainingData[i].y - this.remainingData[i - 1].y; speeds.push({ x: this.remainingData[i].x, y: speed }); } this.speedData = speeds; }, updateChart() { this.chart = new Chart( document.getElementById('myChart').getContext('2d'), { type: 'line', data: { datasets: [ { label: 'Remaining', data: this.remainingData, borderColor: 'rgb(75, 192, 192)', tension: 0.1, fill: false }, { label: 'Speed', data: this.speedData, borderColor: 'rgb(255, 99, 132)', tension: 0.1, fill: false } ] }, options: { scales: { x: { type: 'time', time: { unit: 'minute', tooltipFormat: 'yyyy-MM-dd HH:mm', displayFormats: { minute: 'HH:mm' } }, ticks: { source: 'auto', autoSkip: true, maxRotation: 0, } }, y: { beginAtZero: true } }, plugins: { tooltip: { mode: 'index', intersect: false }, legend: { onClick: (e, legendItem, legend) => { const datasetIndex = legendItem.datasetIndex; const dataset = legend.chart.data.datasets[datasetIndex]; if (!dataset || !dataset.data || !Array.isArray(dataset.data)) { console.log('数据集不存在或者未初始化'); return; } dataset.hidden = !dataset.hidden; legend.chart.update(); } } }, responsive: true, maintainAspectRatio: false } } ); } } } </script>
data.json:
[ { "remaining": 32.76, "timestamp": "2024-09-10 11:51:19" }, { "remaining": 32.78, "timestamp": "2024-09-10 11:50:14" }, { "remaining": 32.81, "timestamp": "2024-09-10 11:49:11" }, { "remaining": 32.85, "timestamp": "2024-09-10 11:48:06" }, { "remaining": 32.88, "timestamp": "2024-09-10 11:47:02" }, { "remaining": 32.9, "timestamp": "2024-09-10 11:45:57" }, { "remaining": 32.93, "timestamp": "2024-09-10 11:44:53" }, { "remaining": 32.95, "timestamp": "2024-09-10 11:43:49" }, { "remaining": 32.98, "timestamp": "2024-09-10 11:42:46" }, { "remaining": 33.0, "timestamp": "2024-09-10 11:41:41" }, { "remaining": 33.03, "timestamp": "2024-09-10 11:40:36" }, { "remaining": 33.05, "timestamp": "2024-09-10 11:39:32" }, { "remaining": 33.08, "timestamp": "2024-09-10 11:38:27" }, { "remaining": 33.1, "timestamp": "2024-09-10 11:37:22" }, { "remaining": 33.13, "timestamp": "2024-09-10 11:36:17" }, { "remaining": 33.15, "timestamp": "2024-09-10 11:35:12" }, { "remaining": 33.2, "timestamp": "2024-09-10 11:34:07" }, { "remaining": 33.22, "timestamp": "2024-09-10 11:33:02" }, { "remaining": 33.24, "timestamp": "2024-09-10 11:31:57" }, { "remaining": 33.26, "timestamp": "2024-09-10 11:30:52" }, { "remaining": 33.29, "timestamp": "2024-09-10 11:29:47" }, { "remaining": 33.31, "timestamp": "2024-09-10 11:28:42" }, { "remaining": 33.33, "timestamp": "2024-09-10 11:27:37" }, { "remaining": 33.36, "timestamp": "2024-09-10 11:26:32" }, { "remaining": 33.38, "timestamp": "2024-09-10 11:25:28" }, { "remaining": 33.4, "timestamp": "2024-09-10 11:24:23" }, { "remaining": 33.43, "timestamp": "2024-09-10 11:23:18" }, { "remaining": 33.45, "timestamp": "2024-09-10 11:22:15" }, { "remaining": 33.47, "timestamp": "2024-09-10 11:21:12" }, { "remaining": 33.52, "timestamp": "2024-09-10 11:20:07" }, { "remaining": 33.54, "timestamp": "2024-09-10 11:19:04" }, { "remaining": 33.56, "timestamp": "2024-09-10 11:18:02" }, { "remaining": 33.57, "timestamp": "2024-09-10 11:16:57" }, { "remaining": 33.57, "timestamp": "2024-09-10 11:15:52" }, { "remaining": 33.58, "timestamp": "2024-09-10 11:14:47" }, { "remaining": 33.59, "timestamp": "2024-09-10 11:13:42" }, { "remaining": 33.6, "timestamp": "2024-09-10 11:12:39" }, { "remaining": 33.61, "timestamp": "2024-09-10 11:11:34" }, { "remaining": 33.62, "timestamp": "2024-09-10 11:10:31" }, { "remaining": 33.62, "timestamp": "2024-09-10 11:09:26" }, { "remaining": 33.63, "timestamp": "2024-09-10 11:08:21" }, { "remaining": 33.64, "timestamp": "2024-09-10 11:07:16" }, { "remaining": 33.65, "timestamp": "2024-09-10 11:06:12" }, { "remaining": 33.66, "timestamp": "2024-09-10 11:05:07" }, { "remaining": 33.67, "timestamp": "2024-09-10 11:04:04" }, { "remaining": 33.68, "timestamp": "2024-09-10 11:03:00" }, { "remaining": 33.69, "timestamp": "2024-09-10 11:01:55" }, { "remaining": 33.7, "timestamp": "2024-09-10 11:00:50" }, { "remaining": 33.71, "timestamp": "2024-09-10 10:59:46" }, { "remaining": 33.72, "timestamp": "2024-09-10 10:58:41" }, { "remaining": 33.72, "timestamp": "2024-09-10 10:57:36" }, { "remaining": 33.73, "timestamp": "2024-09-10 10:56:33" }, { "remaining": 33.74, "timestamp": "2024-09-10 10:55:30" }, { "remaining": 33.75, "timestamp": "2024-09-10 10:54:28" }, { "remaining": 33.75, "timestamp": "2024-09-10 10:53:25" }, { "remaining": 33.76, "timestamp": "2024-09-10 10:52:20" }, { "remaining": 33.76, "timestamp": "2024-09-10 10:51:15" }, { "remaining": 33.77, "timestamp": "2024-09-10 10:50:11" }, { "remaining": 33.78, "timestamp": "2024-09-10 10:49:06" }, { "remaining": 33.78, "timestamp": "2024-09-10 10:48:01" }, { "remaining": 33.79, "timestamp": "2024-09-10 10:46:56" }, { "remaining": 33.79, "timestamp": "2024-09-10 10:45:51" }, { "remaining": 33.8, "timestamp": "2024-09-10 10:44:45" }, { "remaining": 33.81, "timestamp": "2024-09-10 10:43:40" }, { "remaining": 33.81, "timestamp": "2024-09-10 10:42:35" }, { "remaining": 33.82, "timestamp": "2024-09-10 10:41:32" }, { "remaining": 33.82, "timestamp": "2024-09-10 10:40:27" }, { "remaining": 33.83, "timestamp": "2024-09-10 10:39:24" }, { "remaining": 33.83, "timestamp": "2024-09-10 10:38:56" }, { "remaining": 34.83, "timestamp": "2024-09-10 02:12:51" }, { "remaining": 34.83, "timestamp": "2024-09-10 02:11:44" }, { "remaining": 34.84, "timestamp": "2024-09-10 02:10:38" }, { "remaining": 34.84, "timestamp": "2024-09-10 02:09:31" }, { "remaining": 34.84, "timestamp": "2024-09-10 02:08:28" }, { "remaining": 34.85, "timestamp": "2024-09-10 02:07:21" }, { "remaining": 34.85, "timestamp": "2024-09-10 02:06:16" }, { "remaining": 34.86, "timestamp": "2024-09-10 02:05:11" }, { "remaining": 34.86, "timestamp": "2024-09-10 02:04:06" }, { "remaining": 34.87, "timestamp": "2024-09-10 02:03:03" }, { "remaining": 34.87, "timestamp": "2024-09-10 02:01:58" }, { "remaining": 34.87, "timestamp": "2024-09-10 02:00:52" }, { "remaining": 34.88, "timestamp": "2024-09-10 01:59:48" }, { "remaining": 34.88, "timestamp": "2024-09-10 01:58:43" }, { "remaining": 34.89, "timestamp": "2024-09-10 01:57:40" }, { "remaining": 34.89, "timestamp": "2024-09-10 01:56:35" }, { "remaining": 34.9, "timestamp": "2024-09-10 01:55:30" }, { "remaining": 34.92, "timestamp": "2024-09-10 01:54:27" }, { "remaining": 34.94, "timestamp": "2024-09-10 01:53:22" }, { "remaining": 34.96, "timestamp": "2024-09-10 01:52:18" }, { "remaining": 34.98, "timestamp": "2024-09-10 01:51:15" }, { "remaining": 35.01, "timestamp": "2024-09-10 01:50:09" }, { "remaining": 35.03, "timestamp": "2024-09-10 01:49:02" }, { "remaining": 35.05, "timestamp": "2024-09-10 01:47:59" }, { "remaining": 35.05, "timestamp": "2024-09-10 01:46:55" }, { "remaining": 35.05, "timestamp": "2024-09-10 01:45:50" }, { "remaining": 35.06, "timestamp": "2024-09-10 01:44:46" }, { "remaining": 35.08, "timestamp": "2024-09-10 01:43:41" }, { "remaining": 35.1, "timestamp": "2024-09-10 01:42:38" }, { "remaining": 35.12, "timestamp": "2024-09-10 01:41:34" }, { "remaining": 35.14, "timestamp": "2024-09-10 01:40:31" }, { "remaining": 35.15, "timestamp": "2024-09-10 01:39:26" }, { "remaining": 35.17, "timestamp": "2024-09-10 01:38:21" }, { "remaining": 35.19, "timestamp": "2024-09-10 01:37:18" }, { "remaining": 35.21, "timestamp": "2024-09-10 01:36:13" }, { "remaining": 35.22, "timestamp": "2024-09-10 01:35:10" }, { "remaining": 35.22, "timestamp": "2024-09-10 01:34:05" }, { "remaining": 35.26, "timestamp": "2024-09-10 01:31:29" }, { "remaining": 35.28, "timestamp": "2024-09-10 01:30:24" }, { "remaining": 35.3, "timestamp": "2024-09-10 01:29:21" }, { "remaining": 35.31, "timestamp": "2024-09-10 01:28:26" }, { "remaining": 35.33, "timestamp": "2024-09-10 01:27:24" }, { "remaining": 35.35, "timestamp": "2024-09-10 01:26:19" }, { "remaining": 35.37, "timestamp": "2024-09-10 01:25:13" }, { "remaining": 35.38, "timestamp": "2024-09-10 01:24:09" }, { "remaining": 35.39, "timestamp": "2024-09-10 01:23:03" }, { "remaining": 35.41, "timestamp": "2024-09-10 01:21:58" }, { "remaining": 35.43, "timestamp": "2024-09-10 01:20:53" }, { "remaining": 35.47, "timestamp": "2024-09-10 01:19:06" }, { "remaining": 35.49, "timestamp": "2024-09-10 01:18:01" } ]
修正要点
-
使用箭头函数在
setInterval
中绑定正确的this
上下文:- 使用
setInterval(() => { this.fetchData(); }, 60000)
,确保fetchData
在每次调用时使用正确的组件上下文,避免函数指针错误。
- 使用
-
防止重复创建图表:
- 检查
this.chart
是否已存在,只有在首次创建时调用updateChart
,在后续更新中只调用this.chart.update()
,防止不断创建新图表导致的栈溢出。
- 检查
-
优化
fetchData
的数据处理逻辑:- 确保每次数据更新只更新已有图表,不会触发多余的重绘或递归调用。
通过以上优化,可以避免无限递归和重复调用引发的 Maximum call stack size exceeded
错误。