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

Vue3集成ChartJS更新数据时出现Maximum call stack size exceeded错误

Vue3 + ChartJS 更新数据时触发栈溢出的解决方法

问题分析

从报错栈能明确看到,Vue的响应式系统与ChartJS的数据访问逻辑形成了无限循环:ChartJS读取数据时触发Vue的响应式getter,而Vue的响应式拦截又会调用ChartJS的value属性访问,反复调用最终导致栈溢出。该问题仅在结合encryptedDataStore的响应式数据时出现,核心原因是直接将响应式对象传递给了ChartJS。

核心解决方案

给ChartJS传递非响应式的数据副本,切断响应式关联。可通过以下两种方式实现:

  • 使用Vue提供的toRaw()方法获取响应式对象的原始值
  • 通过扩展运算符([...])或Array.from()拷贝数组,用{...obj}拷贝对象

针对两种实现的修改示例

1. 原生ChartJS实现修改

修改onMounted中的Chart初始化和数据更新逻辑,确保传递给Chart的是普通数据:

onMounted(() => {
  // 获取原始非响应式数据
  const rawPasswordData = toRaw(encryptedDataStore.currentAndSafePasswords)
  const rawValueData = toRaw(encryptedDataStore.currentAndSafeValues)

  // 初始化Chart时使用非响应式数据
  const chartInstance = new Chart(
    document.getElementById('passwordStrengthProgressChart') as HTMLCanvasElement,
    {
      type: 'line',
      data: {
        labels: [...rawPasswordData.current], // 拷贝数组切断响应式
        datasets: [
          {
            label: 'Secure Passwords',
            data: [...rawPasswordData.safe],
            borderColor: passwordColor,
            cubicInterpolationMode: 'monotone',
            tension: 0.4
          },
          {
            label: 'Secure Values',
            data: [1, 2],
            borderColor: valuesColor,
            cubicInterpolationMode: 'monotone'
          }
        ]
      },
      options: {
        responsive: false,
        elements: {
          point: {
            radius: 0
          }
        }
      }
    }
  )

  // 测试更新时,同样传递非响应式数据
  const interval = setInterval(() => {
    createTestData()
    // 更新数据时重新获取原始数据并拷贝
    const newRawPasswordData = toRaw(encryptedDataStore.currentAndSafePasswords)
    const newRawValueData = toRaw(encryptedDataStore.currentAndSafeValues)
    
    chartInstance.data.labels = [...newRawPasswordData.current]
    chartInstance.data.datasets[0].data = [...newRawPasswordData.safe]
    chartInstance.data.datasets[1].data = [...newRawValueData.safe]
    chartInstance.update() // 手动触发Chart更新
    clearInterval(interval)
  }, 5000)
})

2. vue-chartjs实现修改

确保data返回的是普通对象,不要直接引用响应式Ref,同时监听数据变化并更新:

setup() {
  const encryptedDataStore: EncryptedDataStore = useEncryptedDataStore();

  const passwordColor: string = PasswordsColor.primaryColor;
  const valuesColor: string = ValuesColor.primaryColor;

  // 定义普通ref存储Chart需要的数据,而非直接引用store的响应式数据
  const chartData = ref({
    labels: [],
    datasets: [
      {
        label: 'Secure Passwords',
        data: [],
        borderColor: passwordColor,
        cubicInterpolationMode: 'monotone',
        tension: 0.4
      },
      {
        label: 'Secure Values',
        data: [],
        borderColor: valuesColor,
        cubicInterpolationMode: 'monotone',
      }
    ]
  })

  const options: any = {
    responsive: true,
    elements: {
      point: {
        radius: 1
      }
    }
  };

  // 初始化和监听store数据变化,更新chartData的普通数据
  const updateChartData = () => {
    const rawPasswordData = toRaw(encryptedDataStore.currentAndSafePasswords)
    const rawValueData = toRaw(encryptedDataStore.currentAndSafeValues)
    chartData.value.labels = [...rawPasswordData.current]
    chartData.value.datasets[0].data = [...rawPasswordData.safe]
    chartData.value.datasets[1].data = [...rawValueData.safe]
  }

  onMounted(() => {
    updateChartData()
    const interval = setInterval(() => {
      createTestData()
      updateChartData() // 调用更新方法
      clearInterval(interval);
    }, 5000);
  })

  // 如果store数据会自动变化,添加watch监听
  watch(
    () => [encryptedDataStore.currentAndSafePasswords, encryptedDataStore.currentAndSafeValues],
    () => {
      updateChartData()
    },
    { deep: true }
  )

  return {
    options,
    data: chartData
  }
}

关键注意点

  • 禁止直接将Vue的响应式Ref/Reactive对象传递给ChartJS,必须转换成普通数据
  • 使用toRaw()时要确保仅用于读取原始数据,避免意外修改响应式状态
  • 对于vue-chartjs,传递的data需是普通响应式对象(内部数据为非响应式的数组/对象),而非嵌套的响应式Ref

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:15:32