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

使用vue-chartjs构建响应式图表时触发“Maximum call stack size exceeded”错误

解决Vue3 + vue-chartjs v4中更新数据时的栈溢出问题

我之前在使用vue-chartjs配合Vue3做实时更新图表的时候,也碰到过几乎一模一样的栈溢出问题,咱们来一步步拆解原因和解决方案:

问题根源分析

你遇到的Maximum call stack size exceeded本质是响应式对象的无限递归访问,触发点主要在这几个地方:

  • 你把已经是响应式的props.device又用reactive重新包装了一遍,导致响应式嵌套,Vue在追踪依赖时出现混乱
  • chartLabels和chartValues用了reactive数组,当这些数组被传入chart.js后,chart.js内部的工具函数在访问数组属性时,反复触发Vue的响应式getter,形成无限递归
  • LineChart组件的template里错误地使用了this.props,在<script setup>语法糖里this是不存在的,虽然这不是栈溢出的直接原因,但也是需要修正的问题

具体解决方案

1. 移除不必要的reactive包装

props本身已经是Vue3的响应式对象,不需要再用reactive二次包装,直接使用props.device即可:

// 删掉这行冗余代码
// const device = reactive<DeviceDistance>(props.device);

// 直接在computed里用props.device
const currentDistance = computed<number>((): number => {
  const tmpDistance = props.device.data?.distance;
  return tmpDistance && tmpDistance < 255 ? tmpDistance : 183;
});

2. 将reactive数组替换为ref数组

reactive数组在被第三方库(比如chart.js)访问时,容易触发意外的响应式追踪,换成ref数组能避免这个问题:

// 把reactive改成ref
const chartLabels = ref<string[]>([]);
const chartValues = ref<number[]>([]);

// 在setInterval里访问数组要加.value
setInterval(() => {
  if (!currentDistance.value) return;
  chartLabels.value.push(new Date().toString())
  chartValues.value.push(currentDistance.value)
  if (chartValues.value.length > 10) {
    chartLabels.value.shift();
    chartValues.value.shift();
  }
}, 500)

3. 优化computed的图表数据返回

确保传给chart.js的是普通的JavaScript数组,而不是响应式包装对象,避免chart.js内部触发无限getter调用:

const chartDataComp = computed<ChartData>((): ChartData => {
  return {
    labels: [...chartLabels.value], // 展开成普通数组
    datasets: [
      {
        label: 'Distance',
        backgroundColor: '#f87979',
        tension: 0.5,
        data: [...chartValues.value] // 同样展开成普通数组
      }
    ]
  }
});

4. 修正LineChart组件的template语法

在<script setup>里不需要用this,直接访问props即可:

<template>
  <Line 
    chartId="line-chart" 
    :width="400" 
    :height="200" 
    :chart-data="props.chartData" 
    :chart-options="props.chartOptions" 
    :plugins="plugins" 
  />
</template>

修改后的完整代码示例

LineChart.vue

<script setup lang="ts">
import { Line } from 'vue-chartjs'
import { Chart as ChartJS, Title, Tooltip, Legend, LineElement, LinearScale, PointElement, CategoryScale } from 'chart.js'
import type {ChartOptions,ChartData} from 'chart.js'

interface Props {
  chartData: ChartData
  chartOptions: ChartOptions
}

const props = defineProps<Props>();

ChartJS.register(
  Title,
  Tooltip,
  Legend,
  LineElement,
  LinearScale,
  PointElement,
  CategoryScale
)

const plugins = [];
</script>

<template>
  <Line 
    chartId="line-chart" 
    :width="400" 
    :height="200" 
    :chart-data="props.chartData" 
    :chart-options="props.chartOptions" 
    :plugins="plugins" 
  />
</template>

DistanceWidget.vue

<script setup lang="ts">
import {computed, ref} from "vue";
import type DeviceDistance from "../../../model/DeviceDistance.js";
import LineChart from "../../chart/LineChart.vue";
import type {ChartData} from "chart.js";

interface Props {
  device: DeviceDistance
}

const props = defineProps<Props>();

const currentDistance = computed<number>((): number => {
  const tmpDistance = props.device.data?.distance;
  return tmpDistance && tmpDistance < 255 ? tmpDistance : 183;
});

const chartLabels = ref<string[]>([]);
const chartValues = ref<number[]>([]);

setInterval(() => {
  if (!currentDistance.value) return;
  chartLabels.value.push(new Date().toString())
  chartValues.value.push(currentDistance.value)
  if (chartValues.value.length > 10) {
    chartLabels.value.shift();
    chartValues.value.shift();
  }
}, 500)

const chartDataComp = computed<ChartData>((): ChartData => {
  return {
    labels: [...chartLabels.value],
    datasets: [
      {
        label: 'Distance',
        backgroundColor: '#f87979',
        tension: 0.5,
        data: [...chartValues.value]
      }
    ]
  }
});

const chartOptions = {
  responsive: true,
  maintainAspectRatio: false,
}
</script>

<template>
  <LineChart :chartData="chartDataComp" :chartOptions="chartOptions" />
</template>

这样修改后,图表应该就能正常实时更新数据,不会再出现栈溢出的问题了。核心思路就是避免响应式对象的不必要嵌套,同时确保传给第三方库的是普通的JavaScript值,而不是响应式包装对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 00:32:45