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

Vue+Chart.js调用chart.update()时报Uncaught TypeError: item is undefined

问题排查:Vue + Chart.js调用chart.update()触发"item is undefined"错误

问题描述

在Vue 3 + Vite(Node v20.13.1)环境下使用Chart.js开发页面,初始状态图表和滑块功能正常,但调用chart.update()时会触发Uncaught TypeError: item is undefined错误,导致滑块无法更新图表。

错误栈信息

Uncaught TypeError: item is undefined
configure core.layouts.js:332
beforeUpdate plugin.subtitle.js:28
callback helpers.core.ts:109
_notify core.plugins.js:60
notify core.plugins.js:42
notifyPlugins core.controller.js:1153
update core.controller.js:508
updateCharts DoughnutChart.vue:66
updateCharts DoughnutChart.vue:62
handleSliderChange DoughnutChart.vue:119
callWithErrorHandling runtime-core.esm-bundler.js:195
callWithAsyncErrorHandling runtime-core.esm-bundler.js:202
emit runtime-core.esm-bundler.js:725
syncValueByPos vue-slider.vue:504
dragMove vue-slider.vue:548

相关代码片段

<script setup>
import { ref, onMounted } from 'vue';
import VueSlider from 'vue-slider-component';
import 'vue-slider-component/theme/default.css';
import Chart from 'chart.js/auto';

const data1 = {
    2010: [50, 30, 20],
    2011: [45, 35, 20]
    // <...>
};

const minYear = 2010;
const maxYear = 2024;
const canvasRefs = ref([]);
const charts = ref([]);
const isPlaying = ref(false);
const currentYear = ref(minYear);
let animationFrame = null;

function createChart(ctx, initialData) {
  return new Chart(ctx, {
    type: 'doughnut',
    data: {
      labels: ['Male', 'Female', 'NB'],
      datasets: [{
        data: initialData,
        backgroundColor: ['#36A2EB', '#FF6384', '#FFCE56'],
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      animation: {
        animateRotate: true,
        animateScale: true
      },
      plugins: {
        tooltip: {
          enabled: true
        }
      }
    }
  });
}

function updateCharts() {
  charts.value.forEach((chartData, index) => {
    const chart = chartData.chart;
    const data = chartData.data[currentYear.value];
    chart.data.datasets[0].data = data;
    chart.update();
  });
}

function playSlider() {
  if (isPlaying.value) {
    cancelAnimationFrame(animationFrame);
    document.getElementById('playButton').textContent = 'Play';
  } else {
    const duration = 15000;
    const steps = (maxYear - minYear) * 100;
    const intervalTime = duration / steps;

    let currentStep = currentYear.value;
    const animate = () => {
      if (currentStep >= maxYear) {
        cancelAnimationFrame(animationFrame);
        isPlaying.value = false;
        document.getElementById('playButton').textContent = 'Play';
      } else {
        currentStep += 0.01;
        currentYear.value = Math.round(currentStep);
        updateCharts();
        animationFrame = requestAnimationFrame(animate);
      }
    };
    animationFrame = requestAnimationFrame(animate);
    document.getElementById('playButton').textContent = 'Pause';
  }
  isPlaying.value = !isPlaying.value;
}

function addChart(data) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const newChart = createChart(ctx, data[currentYear.value]);
  const chartWrapper = document.createElement('div');
  const canvasContainer = document.createElement('div');

  chartWrapper.classList.add('chart-wrapper');
  canvasContainer.classList.add('canvas-container');

  canvasContainer.appendChild(canvas);
  chartWrapper.appendChild(canvasContainer);
  document.getElementById('chartContainers').appendChild(chartWrapper);

  charts.value.push({ chart: newChart, data: data });
  updateCharts();
}

function handleSliderChange(value) {
  if (value >= minYear && value <= maxYear && value != currentYear.value) {
    currentYear.value = value;
    updateCharts();
  }
}

onMounted(() => {
  addChart(data1);
});

</script>

<template>
  <div id="main">
    <div id="chartContainers" class="chart-container">
      <div v-for="(chartData, index) in chartDataList" :key="index" class="chart-wrapper">
        <div class="canvas-container">
          <canvas :ref="el => canvasRefs[index] = el" :id="'chartCanvas-' + index"></canvas>
        </div>
      </div>
    </div>
    <vue-slider :min="minYear" :max="maxYear" :interval="1" :value="currentYear" @change="handleSliderChange"/>
    <p id="currentYear">Year: {{ currentYear }}</p>
    <button id="playButton" @click="playSlider">{{ isPlaying ? 'Pause' : 'Play' }}</button>
  </div>
</template>

<style>
  // <...>
</style>

排查与解决建议

  • 检查数据完整性:确认data1对象包含2010到2024所有年份的数组数据。如果currentYear.value对应的年份不存在数据,chartData.data[currentYear.value]会返回undefined,赋值给数据集后Chart.js更新时会触发报错。
  • 统一DOM渲染逻辑:模板中使用chartDataList循环渲染canvas,但代码里通过原生DOM手动创建图表并添加到容器,这会导致Vue响应式系统与手动DOM操作冲突。建议改用响应式列表管理图表数据,通过Vue模板渲染canvas元素,避免手动操作DOM。
  • 添加数据存在性校验:在updateCharts函数中增加数据检查,避免给数据集赋值undefined:
    function updateCharts() {
      charts.value.forEach((chartData, index) => {
        const chart = chartData.chart;
        const data = chartData.data[currentYear.value];
        if (!data) return; // 数据不存在则跳过更新
        chart.data.datasets[0].data = [...data]; // 解构数组确保数据引用更新
        chart.update();
      });
    }
    
  • 排除插件干扰:错误栈指向plugin.subtitle.js,而代码中并未配置副标题插件。Chart.js/auto会自动注册所有内置插件,可能引发冲突。可以手动导入所需模块替代auto:
    import Chart from 'chart.js';
    import { DoughnutController } from 'chart.js';
    import { Tooltip } from 'chart.js';
    Chart.register(DoughnutController, Tooltip);
    
  • 优化滑块动画逻辑:playSlider中通过requestAnimationFrame每一帧更新年份,会触发过于频繁的图表更新,不仅可能引发错误,还会影响性能。建议调整步长或使用setTimeout限制更新频率,比如每100ms更新一次:
    // 替换原animate函数逻辑
    const animate = () => {
      if (currentStep >= maxYear) {
        clearTimeout(animationFrame);
        isPlaying.value = false;
        document.getElementById('playButton').textContent = 'Play';
      } else {
        currentStep += 1; // 直接按年份步进,避免小数精度问题
        currentYear.value = currentStep;
        updateCharts();
        animationFrame = setTimeout(animate, duration / (maxYear - minYear));
      }
    };
    
  • 移除重复更新调用:addChart函数最后调用的updateCharts()是不必要的,因为图表刚创建时已经传入了初始数据,重复更新可能导致异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 09:00:55