在 vue.js 中更改复杂的计算对象

Posted

技术标签:

【中文标题】在 vue.js 中更改复杂的计算对象【英文标题】:Changing complex computed object in vue.js 【发布时间】:2018-04-25 20:44:42 【问题描述】:

我有一张桌子的复杂对象。看起来像这样:


    1510002000: 
        date: "07.11.17"
        hours: 
            1510002000:
                activity:
                    activity: "Тест",
                    color: "#00ff00",
                    end_at: 1510005600,
                    start_at: 1510002000,
                    type_id: 1
                
            ,
            1510005600: 
                ...
            ,
            ...
        
    ,
    ....

这是使用此对象的模板代码:

    <tr v-for="date in this.tds">
        <td> date.date </td>
        <td is="hour-td"
            v-for="hour in date.hours"
            :color="hour.activity.color"
            :start_at="hour.activity.start_at"
            :end_at="hour.activity.end_at"
            :activity="hour.activity.activity"
            :type_id="hour.activity.type_id"
        >
        </td>
    </tr>

我将它评估为一个计算属性,但是当父组件提供数据异步时我需要重新渲染表,所以我有一个 prop 的观察者(prop 称为“活动”):

watch: 
    activities: function()
        var vm  = this;
        let dth = new DateTimeHelper;

        if (this.activities.length > 0)
            this.activities.forEach(function(activity)
                let dateTimestamp = dth.getDateTimestampFromTimestamp(activity.start_at); // just getting the key

                if (vm.tds[dateTimestamp])
                    if (vm.tds[dateTimestamp].hours[activity.start_at])
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.color    = activity.color;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id  = activity.type_id;
                    
                
            );
        

        console.log(vm.tds) // here I can see that the object is filled with new data
    
,

问题是表格没有重新渲染。更准确地说,组件“hour-td”不包含新数据。 我也尝试过使用 Vue.set,但没有成功 你能帮我更新表格吗?我花了大约 5 个小时进行重构和尝试。 提前致谢 解决方案 就我而言,可以有两种状态:有活动,没有活动。所以我为每种情况制作了两个计算道具并分别渲染它们并通过 v-if="activities.length"

进行切换

【问题讨论】:

仅从您发布的代码中很难了解全局,但我之前使用过日期和计算属性,Vue 不会将 Date 更改为更新。计算的属性被缓存,并且只有在它们的依赖项更新时才会更新,所以这可能是你的 hour-td 组件不工作的原因。我要尝试的第一件事是根据计算属性中的时间戳创建一个new Date() 是的,你说得对缓存。但问题不是日期(它总是在时间戳中,所以我像处理数字一样工作,它们也来自服务器)而不是缓存(我使用观察者,重写缓存)。在这里你可以找到组件的完整代码github.com/Vasek18/my-hours.ru/blob/master/resources/assets/js/… 【参考方案1】:

我认为您的问题是与数组直接分配的 Change Detection Caveats(您可以阅读 here)的 Vue 已知问题有关,它不会检测到更改。

您应该更改这部分代码(使用直接数组赋值):

if (vm.tds[dateTimestamp])
   if (vm.tds[dateTimestamp].hours[activity.start_at])
      vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
      vm.tds[dateTimestamp].hours[activity.start_at].activity.color    = activity.color;
      vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id  = activity.type_id;
   

使用数组的 Vue.set() 选项来检测更改并重新渲染组件。它在不同的场合对我有用:

// Vue.set(object, key, value)
// something like this: 
Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity.activity,1,activity.activity);

更多信息在这里:https://codingexplained.com/coding/front-end/vue-js/array-change-detection

编辑:

我现在明白你说的是:

我也尝试过使用 Vue.set,但没有成功

你的意思是:“没有成功”?可以分享一下代码吗?我有同样的问题,我用 Vue.set 解决了..

您也可以查看vm.$forceUpdate(),尝试在最后一个console.log 之后执行或获取vm.$nextTick( [callback] ) 中的所有代码以执行所有操作(加载表中的数据)然后,在下一个刻度上重新渲染组件。

更多信息在这里:https://vuejs.org/v2/api/#vm-forceUpdate && https://vuejs.org/v2/api/#Vue-nextTick

编辑2:

我认为你的问题出在数组的索引上,你应该看看这里:https://vuejs.org/v2/guide/list.html#Array-Change-Detection。 尝试更改:

if (vm.tds[dateTimestamp])
                    if (vm.tds[dateTimestamp].hours[activity.start_at])
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.activity = activity.activity;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.color    = activity.color;
                        vm.tds[dateTimestamp].hours[activity.start_at].activity.type_id  = activity.type_id;
                    
                

并简化为:

if (vm.tds[dateTimestamp] && vm.tds[dateTimestamp].hours[activity.start_at])
   Vue.set( vm.tds, vm.tds.indexOf(vm.tds[dateTimestamp].hours[activity.start_at]), activity);

希望对你有帮助!

【讨论】:

感谢您的回复,但我仍然无法解决这个问题。所以我尝试了“Vue.set(vm.tds[dateTimestamp].hours[activity.start_at].activity, 'activity', activity.activity);”和“Vue.set(vm.tds, dateTimestamp, vm.tds[dateTimestamp]);”和“让 temp = activity.start_at; Vue.set(vm.tds[dateTimestamp].hours, activity.start_at, key: activity: activity);”在循环中。没有结果。也可以试试“this.$children.forEach(function(child) child.$forceUpdate(); );”。也没有成功 感谢您的帮助。这种方法行不通,因为它不是数组,而是对象,而且,我的理解是,vue 无法在对象中的对象中做出反应性的对象。所以我有另一个解决方案。活动存在的情况下的两个单独的道具。你可以在这里看到它github.com/Vasek18/my-hours.ru/blob/master/resources/assets/js/… 好的!我很高兴你找到了解决方案。尝试编辑并回答您自己的问题,以帮助将来遇到同样问题的人!如果答案有帮助,请点赞;)

以上是关于在 vue.js 中更改复杂的计算对象的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js 如何从方法更改计算属性?

Vue.js(05):计算与监听属性

Vue.js学习日记——计算属性和侦听器

Vue.js - 如何在数组对象上实现计算属性?

在 Vue JS 中使用计算与动态对象

05Vue.js---计算属性