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
相关产品推荐
相关产品推荐

