Vue没有根据方法更新计算属性,但是直接调用时相同的方法返回正确的值

Posted

技术标签:

【中文标题】Vue没有根据方法更新计算属性,但是直接调用时相同的方法返回正确的值【英文标题】:Vue not updating computed property based on a method, but the same method returns correct value when called directly 【发布时间】:2020-11-30 01:08:45 【问题描述】:

我有一个用于提交表单的简单按钮组件,单击它时应该会给出一些反馈:在提交表单时显示一个微调器并禁用按钮,如果返回任何错误,也保持禁用它从服务器直到错误被清除(通过更改包含错误的字段的输入来清除错误)。

这是组件的模板:

<template>
    <button type="submit" :disabled="disabled">
        <loading-spinner v-if="submitting"></loading-spinner>
        <slot>Submit</slot>
    </button>
</template>

<script>
    export default 
        props: 
            form: 
                type: Object,
                required: true
            
        ,
        created() 
            // Globally available event handler which listens for events 
            // emitted by Form object - it works fine
            Event.$on('submitting', () => this.submitting = true);
            Event.$on('submitted', () => this.submitting = false);
        ,
        data() 
            return 
                submitting: false,
            
        ,
        computed: 
            // Here is the problem: this.form.errors.any() doesn't get updated in the 
            // computed prop even when errors are cleared. This same method, however,
            // returns correct result when called directly via a test button
            disabled() 
                return this.submitting || this.form.errors.any()
            ,
              // doesn't get updated either - apparently
//            errors() 
//                return this.form.errors.any()
//            
        ,
    
</script>

按钮将Form 对象传递给它,因此它也可以访问Errors 对象,该对象具有一些方便的方法来管理错误。作为 prop 传递的 Form 对象在 submit-button 组件内是响应式的,当我在表单中(在父组件上)键入时,它的字段会在 Vue Devtools 中更新,但计算出来的 prop(disablederrors ) 依赖它不会得到更新。

当我提交带有错误数据的表单时,我得到了错误,所以form.errors.any() 返回真。但是,一旦第一次返回错误,按钮就会被禁用并像那样“冻结”(form.errors.any() 会一直返回 true,即使它不是)。

我知道计算的 props 被缓存了,但是根据定义,当它们的依赖更新时它们应该更新(在这种情况下,计算属性 disabled 应该更新,因为 this.form.errors.any() 已更新)。但是,提交按钮组件似乎“忽略”了其计算属性中 this.form.errors.any() 的更新(一旦出现错误,它就会一直返回 true,并且永远不会更新)。如果我在父组件上创建相同的计算属性,也会发生同样的事情。

其他信息

当我手动检查其值时(例如,通过调用 form.errors.any() 的按钮),它每次都会将正确的值记录到控制台,但它不会在我的计算属性中得到更新。

Errors 对象上的 any() 方法 - 这里没什么特别的

any() 
    return Object.keys(this.errors).length > 0;

Form 和 Errors 对象都可以正常工作:它们的方法在从任何组件直接调用(例如通过按钮单击)时返回正常值,并且它们按预期调度事件,它们只是不对计算的 prop 做出反应。

解决方法

我的解决方案是使用方法而不是计算属性,但它有点过于冗长,我最想知道为什么在这种情况下我不能使用更优雅的计算属性?

<template>
    <button type="submit" :disabled="submitting || anyErrors()">
        <loading-spinner v-if="submitting"></loading-spinner>
        <slot>Submit</slot>
    </button>
</template>

<script>
    export default 
        ...

        methods: 
            anyErrors() 
                return this.form.errors.any()
            
        
    
</script>

【问题讨论】:

我会说这是由于以 Vue 无法检测到的方式改变对象引起的(请参阅docs)。您如何填充 Errors 对象中的错误?您是否正在向对象添加新属性? @DecadeMoon 错误是 Errors 类中的一个对象。但是,如果你问它,它不是 Vue 组件(因为我理解文档中的章节指的是那个)。 this.errors 是实例化(构造函数)上的空对象,可以通过方法(get/register/clear 和已经提到的任何方法)访问/填充/清除它。 【参考方案1】:

您需要将 form.errors 属性放置在表单的 props 上,以使其开箱即用。否则,当您添加错误时,您必须使用Vue.set() 方法才能使其具有反应性。

所以你的 props 对象应该是这样的:

props: 
     form: 
         type: Object,
         required: true,
         errors: ,
     
,

更多信息: https://vuejs.org/v2/guide/reactivity.html

(编辑以使错误成为对象,如问题中所示,而不是数组)

【讨论】:

仍然不适用于计算属性。我还尝试仅传递错误对象(而不是整个表单,因为我实际上并不需要它,我只使用其中的错误)但它没有检测到它 你试过用Vue.set()吗?看看我发布的链接。这对我来说仍然是一个反应性问题,因为数据在那里它只是没有在屏幕上更新。 嗨,@Ashley,是的,我试过了,但还是不行。这可能是因为我没有正确设置它。我应该在 created/mounted 生命周期挂钩中调用 this.$set 吗?据我了解,您调用要设置反应性的对象,给它一个属性和一个值。所以它应该看起来像:this.$set(this.form.errors, 'hasErrors', this.form.errors.any())?但是新的“hasErrors”属性仍然没有反应。我一定是在这里做错了什么,你愿意更彻底地解释一下吗?谢谢你,很抱歉这么晚才回复!

以上是关于Vue没有根据方法更新计算属性,但是直接调用时相同的方法返回正确的值的主要内容,如果未能解决你的问题,请参考以下文章

深入Vue的响应式原理

vue.js中的计算属性(computed)methodswatched三者的区别

Vue.js计算属性不更新

Vue计算属性

vue 计算属性 实例选项 生命周期

关于vue的使用计算属性VS使用计算方法的问题