在Vue中访问子组件的计算属性

时间:2018-02-23 15:00:05

标签: javascript vue.js vuejs2 vue-component

访问子组件的计算属性的正确方法是什么?以下是我尝试实现的简化示例(也可在JSFiddle上获得):



const FoobarWidget = {
    template: '<li>a: <input type="text" v-model="value.a" style="width:2em;"> b: <input type="text" v-model="value.b" style="width:2em;"> sum: {{this.sum}} <button @click="die">x</button></li>',
    props: {
        value: {
            type: Object,
            required: true,
        }
    },
    computed: {
        sum() {
            const s = Number(this.value.a) + Number(this.value.b)
            // WARNING eslint - vue:no-side-effects-in-computed-properties
            this.value.sum = s;
            return s;
        }
    },
    methods: {
        die() {
            this.$emit('die');
        }
    }
};

new Vue({
    el: '#app',
    data: {
        foobars: [{
            a: '5',
            b: '6'
        }],
    },
    components: {
        FoobarWidget,
    },
    computed: {
        myJson() {
            return JSON.stringify(this.foobars, null, 2);
        }
    },
    methods: {
        addNew() {
            this.foobars.push({
                a: '1',
                b: '2'
            });
        },
        foobarDead(index) {
            this.foobars.splice(index, 1);
        }
    }
});
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
  <button @click="addNew()">add foobar</button>
  <h3>Foobars</h3>
  <ul>
    <foobar-widget v-for="(foobar, index) in foobars" :key="index" @die="foobarDead(index)" v-model="foobars[index]"/>
  </ul>
  <h3>JSON</h3>
  <pre>{{myJson}}</pre>
</div>
&#13;
&#13;
&#13;

在尝试这个例子之后可以看到,它主要起作用 - 除了在更改子组件中的值(a,b和计算的总和)之后它没有很好地反映父组件(生成的JSON)中的变化)。

问题似乎与SO: Computed property on child component props类似,但该问题的OP具有执行字符串格式化的计算值,使用Vue过滤器是一个很好的解决方案。这不是这种情况 - sum()计算属性可以是任意函数,需要在父组件和子组件中访问它。

我上面的方法,通过在重新计算时添加sum属性来修改prop对象绝对不是正确的方法,因此我的问题。它不仅运行困难,而且还产生ESLint警告(上面代码中的WARNING注释)。

2 个答案:

答案 0 :(得分:1)

我建议你采取错误的做法。如果您想要一个属性是对象上值的总和,请将该属性添加到对象,而不是组件。

这是一个例子。

const FoobarWidget = {
  template: '<li>a: <input type="text" v-model="value.a" style="width:2em;"> b: <input type="text" v-model="value.b" style="width:2em;"> sum: {{value.sum}} <button @click="die">x</button></li>',
  props: {
    value: {
      type: Object,
      required: true,
    }
  },
  methods: {
    die() {
      this.$emit('die');
    }
  }
};

const foo = {
  a: 5,
  b: 6,
  // define your property here
  get sum() {
    return +this.a + +this.b
  }
}

new Vue({
  el: '#app',
  data: {
    foobars: [foo],
  },
  components: {
    FoobarWidget,
  },
  methods: {
    addNew() {
      this.foobars.push({
        a: '1',
        b: '2',
        get sum() {
          return +this.a + +this.b;
        }
      });
    },
    foobarDead(index) {
      this.foobars.splice(index, 1);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
  <button @click="addNew()">add foobar</button>
  <h3>Foobars</h3>
  <ul>
    <foobar-widget v-for="(foobar, index) in foobars" :key="index" @die="foobarDead(index)" v-model="foobars[index]" />
  </ul>
  <h3>JSON</h3>
  <pre>{{foobars}}</pre>
</div>

使用此方法,您永远不需要访问组件的计算属性。

答案 1 :(得分:1)

您父母没有更新的原因是您adding a property to an object, which Vue doesn't detect

而不是

    sum() {
        const s = Number(this.value.a) + Number(this.value.b)
        // WARNING eslint - vue:no-side-effects-in-computed-properties
        this.value.sum = s;
        return s;
    }

你会做

sum() {
    const s = Number(this.value.a) + Number(this.value.b)
    this.$set(this.value, 'sum', s);
    return s;
}

尽管计算中存在副作用的代码气味,并且在道具中更新了值,这表明你应该采取不同的做法。