Vue2+vue-chartjs图表数据加载异常:需调窗口/开DevTools才显示
解决Vue 2 + vue-chartjs异步数据加载后图表不渲染的问题
你的问题核心在于:图表在mounted钩子中就执行了renderChart,但此时异步GET请求还没返回数据,datesList和avgList都是空数组。而窗口调整大小或打开开发者工具时,会触发chart.js的响应式重绘逻辑,这时候数据已经加载完成,所以图表才显示出来。
下面给你几个快速解决的方案,从临时应急到规范修复都有:
方案1:临时应急方案(强制触发重绘)
这个方案改动最小,适合快速修复问题。在数据获取并处理完成后,手动触发窗口的resize事件,模拟窗口调整的效果来触发图表重绘:
getEvents () { this.$http.get('http://localhost:8081/users/getAllEvents', { headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('token'), } }) .then((res) => { // 你的原有数据处理逻辑保持不变 var eventsArr = res.body; var arrayLength = eventsArr.length; for (var i = 0; i < arrayLength; i++) { var date = new Date(parseInt(eventsArr[i].startTime)) var day = date.getDate() var month = date.getMonth() var year = date.getFullYear() var hours = date.getHours() hours = ("0" + hours).slice(-2); var minutes = date.getMinutes() minutes = ("0" + minutes).slice(-2); var str = day + "." + (month + 1) + "." + year +" - " + hours + ":" + minutes this.datesList.push(str); } for (var i = 0; i < arrayLength; i++) { var evnt = eventsArr[i]; this.avgList.push({label: evnt.name,y: evnt.pulseAverage ,tag: evnt.tag, id: evnt.id }); } // 数据处理完成后,手动触发resize事件 setTimeout(() => { window.dispatchEvent(new Event('resize')) }, 0) }) }
这里用setTimeout是为了确保数据更新后的DOM已经完成渲染,再触发重绘事件。
方案2:规范修复(数据加载完成后再渲染图表)
更合理的做法是把图表初始化逻辑推迟到数据加载完成之后,避免空数据渲染的问题:
步骤1:封装图表初始化方法
把mounted里的图表渲染逻辑抽成独立方法:
data () { return { gradient: null, gradient2: null, datesList : [], avgList: [], chartInstance: null // 用来保存图表实例,方便后续操作 } }, methods:{ // 新增:封装图表初始化逻辑 initChart() { this.gradient = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450) this.gradient2 = this.$refs.canvas.getContext('2d').createLinearGradient(0, 0, 0, 450) this.gradient.addColorStop(0, 'rgba(255, 0,0, 0.5)') this.gradient.addColorStop(0.5, 'rgba(255, 0, 0, 0.25)'); this.gradient.addColorStop(1, 'rgba(255, 0, 0, 0)'); this.gradient2.addColorStop(0, 'rgba(0, 231, 255, 0.9)') this.gradient2.addColorStop(0.5, 'rgba(0, 231, 255, 0.35)'); this.gradient2.addColorStop(1, 'rgba(0, 231, 255, 0)'); this.chartInstance = this.renderChart({ labels: this.datesList, datasets: [ { label: 'Events', borderColor: '#05CBE1', pointBackgroundColor: 'white', pointBorderColor: 'white', borderWidth: 2, backgroundColor: this.gradient2, data: this.avgList }, ] }, { onClick: function(event){ var activePoints = this.getElementAtEvent(event) var firstPoint = activePoints[0]; if(firstPoint !== undefined){ var label = this.data.labels[firstPoint._index]; var value = this.data.datasets[firstPoint._datasetIndex].data[firstPoint._index]; var location = "eventGraph?id=" + value.id; window.location.href = location; } }, responsive: true, maintainAspectRatio: false, fontColor: '#66226', tooltips: { enabled: true, mode: 'single', callbacks: { title: function(tooltipItems, data) { var evnt = data.datasets[0].data[tooltipItems[0].index].label; return evnt }, label: function(tooltipItems, data) { var avg = 'Average heart: ' + [tooltipItems.yLabel]; var evnt = 'Type: ' + data.datasets[0].data[tooltipItems.index].tag; return [avg,evnt]; } } } }) }, // 原有getEvents方法修改 getEvents () { this.$http.get('http://localhost:8081/users/getAllEvents', { headers: { 'Content-Type': 'application/json', 'Authorization': localStorage.getItem('token'), } }) .then((res) => { // 原有数据处理逻辑保持不变 var eventsArr = res.body; var arrayLength = eventsArr.length; for (var i = 0; i < arrayLength; i++) { var date = new Date(parseInt(eventsArr[i].startTime)) var day = date.getDate() var month = date.getMonth() var year = date.getFullYear() var hours = date.getHours() hours = ("0" + hours).slice(-2); var minutes = date.getMinutes() minutes = ("0" + minutes).slice(-2); var str = day + "." + (month + 1) + "." + year +" - " + hours + ":" + minutes this.datesList.push(str); } for (var i = 0; i < arrayLength; i++) { var evnt = eventsArr[i]; this.avgList.push({label: evnt.name,y: evnt.pulseAverage ,tag: evnt.tag, id: evnt.id }); } // 数据准备好后,调用图表初始化方法 this.initChart() }) } }, mounted () { // 这里不再直接渲染图表,等待数据加载完成后再调用initChart }
额外注意点
- 你当前的
avgList是包含y字段的对象数组,vue-chartjs 2.7.1对应的chart.js 2.x版本是支持这种格式的,不需要担心兼容性问题。 - Vue对数组的
push方法做了响应式处理,所以你直接push数据的方式是没问题的,不需要额外触发更新。
内容的提问来源于stack exchange,提问作者Pelegyair
相关产品推荐
相关产品推荐

