如何让 vue 组件等到数据准备好渲染?

Posted

技术标签:

【中文标题】如何让 vue 组件等到数据准备好渲染?【英文标题】:How to let vue component wait until the data is ready for render? 【发布时间】:2020-08-07 14:37:18 【问题描述】:

vue 组件使用 axios get 不会等待控制器的数据,它会提示错误:

index.vue?d4c7:200 Uncaught (in promise) TypeError: Cannot read property 'ftth' of undefined

我的代码如下:

<template>
    <div class="dashboard-editor-container">
        <el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
            <line-chart :chart-data="lineChartData"/>
        </el-row>
    </div>
</template>

<script>
    import LineChart from './components/LineChart';
    import axios from 'axios';

    const lineChartData = 
        all: 
            FTTHData: [],
            VDSLData: [],
            ADSLData: [],
        ,
    ;
    export default 
        name: 'Dashboard',
        components: 
            LineChart,
        ,
        data() 
            return 
                lineChartData: lineChartData.all,
            ;
        ,
        created() 
            this.getData();
        ,
        methods: 
            handleSetLineChartData(type) 
                this.lineChartData = lineChartData[type];
            ,
            async getData() 
                axios
                    .get('/api/data_graphs')
                    .then(response => 
                        console.log(response.data);
                        var data = response.data;
                        var i = 0;
                        for (i = Object.keys(data).length - 1; i >= 0; i--) 
                            lineChartData.all.FTTHData.push(data[i]['ftth']);
                            lineChartData.all.VDSLData.push(data[i]['vdsl']);
                            lineChartData.all.ADSLData.push(data[i]['adsl']);
                        
                    );
            ,
        ,
    ;
</script>

我必须使用watch方法吗?

【问题讨论】:

请分享您的代码 使用 v-if 和 v-else 来决定是否显示组件。我在从服务器检索数据之前显示了一个加载组件 您正在使用没有等待的异步,但不是这里的问题。 console.log 打印什么? 当 console.log "data[i]['f​​tth']" 它说 "index.vue?d4c7:207 Uncaught (in promise) TypeError: Cannot read property 'ftth' of undefined at eval ”。也许是因为我在控制器中使用了 foreach,这就是为什么将数据传递给 vue 需要时间? @Savlon,经过大量调试。谢谢你的帮助:) 我也用 axios 超时。 【参考方案1】:

首先,因为您有这样一个嵌套的数据结构,所以您需要一个计算属性来返回数据是否已加载。通常,您可以在模板中执行此检查。

computed: 
  isDataLoaded() 
    const nestedLoaded = Object.keys(this.lineChartData).map(key => this.lineChartData[key].length !== 0)
    return this.lineChartData && nestedLoaded.length !== 0
  

您可以使用v-if="isDataLoaded" 隐藏元素,直到数据加载完毕。

【讨论】:

你是个天才,它行得通。没有在 axios 中使用超时。感谢您提供此代码:) 你知道如何让vue-component等待一分钟以上再渲染数据吗?我对数据库的查询需要超过 1 分钟。 我不确定是否有任何用户会坐等 1 分钟等待您的数据加载。您最好考虑优化查询的方法,和/或在后台处理它,同时让用户在您的应用中执行其他操作。【参考方案2】:

目前尚不清楚response.data 的外观,但因为您使用的是Object.keys,所以我假设它是一个对象。

如果您需要遍历键,那么在使用数字索引时您很可能不会得到对象。因此,您需要获取 key 和索引 i 并使用该值来访问该对象。改变这个:

for (i = Object.keys(data).length - 1; i >= 0; i--) 
  lineChartData.all.FTTHData.push(data[i]['ftth']);
  lineChartData.all.VDSLData.push(data[i]['vdsl']);
  lineChartData.all.ADSLData.push(data[i]['adsl']);

到这里:

const keys = Object.keys(data)
for (i = keys.length - 1; i >= 0; i--) 
  lineChartData.all.FTTHData.push(data[keys[i]]['ftth']);
  lineChartData.all.VDSLData.push(data[keys[i]]['vdsl']);
  lineChartData.all.ADSLData.push(data[keys[i]]['adsl']);

但是对于循环对象的键更容易使用:

for (let key in data) 
  lineChartData.all.FTTHData.push(data[key]['ftth']);
  lineChartData.all.VDSLData.push(data[key]['vdsl']);
  lineChartData.all.ADSLData.push(data[key]['adsl']);

替代语法将为您提供密钥,我认为更容易阅读。

【讨论】:

你的回答很有用:)【参考方案3】:

同时:

使用设置 Axios Timeout 5000ms

axios
.get('/api/data_graphs',  timeout: 5000 )
.then(response => 
  console.log(response.data);
  var data = response.data;
  var i = 0;
  for (i = Object.keys(data).length - 1; i >= 0; i--) 
    lineChartData.all.FTTHData.push(data[i]['ftth']);
    lineChartData.all.VDSLData.push(data[i]['vdsl']);
    lineChartData.all.ADSLData.push(data[i]['adsl']);
  
  this.lineChartIsLoaded = true;
);

在 vue 组件中使用 v-if

<line-chart v-if="lineChartIsLoaded" :chart-data="lineChartData" :date-data="dateData" />

将 lineChartIsLoaded 默认设置为 false

const lineChartIsLoaded = false;

【讨论】:

使用 5 秒计时器是一种竞争条件;如果时间超过 5 秒,则无法从本质上解决问题,并且会对您的用户体验造成重大影响。 这也是我的经验,但现在我将使用它,因为我仍在寻找解决方案。 我看错了,我以为你在使用 setTimeout。 axios 设置超时只是当 axios 决定停止尝试获取数据时。您应该可以毫无问题地删除它。使用v-ifisLoaded 变量是正确的解决方案。【参考方案4】:

您可以在加载真实数据之前在数据属性中写入虚拟数据

all: 
  FTTHData: ["Loading..."],
  VDSLData: ["Loading..."],
  ADSLData: ["Loading..."],
,

【讨论】:

以上是关于如何让 vue 组件等到数据准备好渲染?的主要内容,如果未能解决你的问题,请参考以下文章

嵌套组件页面渲染完了还请求不到数据

Angular 2如何让子组件等待异步数据准备好

解决vue开发时子组件数据和组件渲染的异步问题

变量更改时如何重新渲染 vue.js 组件

Selenium 等到文档准备好

如何让 Vue 停止渲染模板注释?