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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:50:40