是否有可能在Vue.Js中传递计算属性中的参数。我可以看到,当有getter /setter使用computed时,他们可以接受一个参数并将其分配给一个变量。比如这里的文档:

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
  
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

这也是可能的吗:

computed: {
  fullName: function (salut) {
    return salut + ' ' + this.firstName + ' ' + this.lastName    
  }
}

其中computed属性接受一个参数并返回所需的输出。然而,当我尝试这样做时,我得到这个错误:

vue.common.js:2250 Uncaught TypeError: fullName不是函数(…)

我是否应该在这种情况下使用方法?


当前回答

我想首先重申前面的警告,使用computed(被缓存的)参数只会使computed不被缓存,实际上只是使它成为一个方法。

然而,话虽如此,这里是我能想到的所有可能有边界情况的变化。如果你将其剪切并粘贴到演示应用中,你就会清楚发生了什么:

<template>
  <div>

    <div style="background: violet;"> someData, regularComputed: {{ someData }}, {{ regularComputed }} </div>
    <div style="background: cornflowerblue;"> someComputedWithParameterOneLine: {{ someComputedWithParameterOneLine('hello') }} </div>
    <div style="background: lightgreen;"> someComputedWithParameterMultiLine: {{ someComputedWithParameterMultiLine('Yo') }} </div>
    <div style="background: yellow"> someComputedUsingGetterSetterWithParameterMultiLine: {{ someComputedUsingGetterSetterWithParameterMultiLine('Tadah!') }} </div>

    <div>
      <div style="background: orangered;"> inputData: {{ inputData }} </div>
      <input v-model="inputData" />
      <button @click="someComputedUsingGetterSetterWithParameterMultiLine = inputData">
        Update 'someComputedUsingGetterSetterWithParameterMultiLine' with 'inputData'.
      </button>
    </div>

    <div style="background: red"> newConcatenatedString: {{ newConcatenatedString }} </div>

  </div>
</template>

<script>

  export default {

    data() {
      return {
        someData: 'yo',
        inputData: '',
        newConcatenatedString: ''
      }
    },

    computed: {

      regularComputed(){
        return 'dude.'
      },

      someComputedWithParameterOneLine(){
        return (theParam) => `The following is the Parameter from *One* Line Arrow Function >>> ${theParam}`
      },

      someComputedWithParameterMultiLine(){
        return (theParam) => {
          return `The following is the Parameter from *Multi* Line Arrow Function >>> ${theParam}`
        }
      },

      // NOTICE that Computed with GETTER/SETTER is now an Object, that has 2 methods, get() and set(), so after the name of the computed we use : instead of ()
      // thus we do: "someComputedUsingGetterSetterWithParameterMultiLine: {...}" NOT "someComputedUsingGetterSetterWithParameterMultiLine(){...}"
      someComputedUsingGetterSetterWithParameterMultiLine: {
        get () {
          return (theParam) => {
            return `As part of the computed GETTER/SETTER, the following is inside get() which receives a Parameter (using a multi-line Arrow Function) >>> ${theParam}`
          }
        },
        set(newSetValue) {
          console.log('Accessing get() from within the set()', this.someComputedUsingGetterSetterWithParameterMultiLine('hello from inside the Setter, using the Getter.'))
          console.log('Accessing newSetValue in set() >>>>', JSON.stringify(newSetValue))
          this.newConcatenatedString = `**(1)${this.someComputedUsingGetterSetterWithParameterMultiLine('hello from inside the Setter, using the Getter.')}**  This is a concatenation of get() value that had a Parameter, with newSetValue **(2)${newSetValue}** that came into the set().`
        }
      },

    },

  }

</script>

其他回答

从技术上讲,我们可以将参数传递给计算函数,就像我们可以在vuex中将参数传递给getter函数一样。这样的函数是返回另一个函数的函数。

例如,在存储的getter中:

{
  itemById: function(state) {
    return (id) => state.itemPool[id];
  }
}

这个getter可以映射到组件的计算函数:

computed: {
  ...mapGetters([
    'ids',
    'itemById'
  ])
}

我们可以在模板中使用这个计算函数,如下所示:

<div v-for="id in ids" :key="id">{{itemById(id).description}}</div>

我们可以应用相同的方法来创建一个接受参数的计算方法。

computed: {
  ...mapGetters([
    'ids',
    'itemById'
  ]),
  descriptionById: function() {
    return (id) => this.itemById(id).description;
  }
}

并在模板中使用它:

<div v-for="id in ids" :key="id">{{descriptionById(id)}}</div>

话虽如此,我并不是说这是Vue的正确方式。

但是,我可以观察到,当具有指定ID的项在存储中发生变化时,视图确实会使用该项的新属性自动刷新其内容(绑定似乎工作得很好)。

是的方法是用来使用参数的。就像上面的答案一样,在你的例子中,最好使用方法,因为执行非常轻。

仅供参考,在方法复杂且成本较高的情况下,可以这样缓存结果:

data() {
    return {
        fullNameCache:{}
    };
}

methods: {
    fullName(salut) {
        if (!this.fullNameCache[salut]) {
            this.fullNameCache[salut] = salut + ' ' + this.firstName + ' ' + this.lastName;
        }
        return this.fullNameCache[salut];
    }
}

注意:使用此方法时,如果处理的是数千,请注意内存

你需要小心vue/no-side-effects-in-computed-properties ESlint规则,不要在computed内部做任何事情。

与此同时,如果您正在寻找一种记忆方法,您可以阅读这篇文章或使用veuse的ememoize。

或者甚至从Vue3.2开始的v-memo。

我没有看到一个清晰的Vue 3的例子,所以我添加了一个我工作的应用程序。首先调用一个方法,然后返回一个计算值。因此,该方法将在Vue重新渲染时被调用,但随后将返回缓存的计算值,并且仅在响应性输入变量发生变化时才执行。

<script setup>
import { computed, ref } from 'vue'

const itemOne = ref(1);
const itemTwo = ref(2);

const getItemDoubled: (key) => {
    return computed(()=> item[key].value * 2);
}
</script>

<template>
    <p>get dynamic name computed value: {{ getItemDoubled('One') }}
    <p>get dynamic name computed value: {{ getItemDoubled('Two') }}
</template

我想首先重申前面的警告,使用computed(被缓存的)参数只会使computed不被缓存,实际上只是使它成为一个方法。

然而,话虽如此,这里是我能想到的所有可能有边界情况的变化。如果你将其剪切并粘贴到演示应用中,你就会清楚发生了什么:

<template>
  <div>

    <div style="background: violet;"> someData, regularComputed: {{ someData }}, {{ regularComputed }} </div>
    <div style="background: cornflowerblue;"> someComputedWithParameterOneLine: {{ someComputedWithParameterOneLine('hello') }} </div>
    <div style="background: lightgreen;"> someComputedWithParameterMultiLine: {{ someComputedWithParameterMultiLine('Yo') }} </div>
    <div style="background: yellow"> someComputedUsingGetterSetterWithParameterMultiLine: {{ someComputedUsingGetterSetterWithParameterMultiLine('Tadah!') }} </div>

    <div>
      <div style="background: orangered;"> inputData: {{ inputData }} </div>
      <input v-model="inputData" />
      <button @click="someComputedUsingGetterSetterWithParameterMultiLine = inputData">
        Update 'someComputedUsingGetterSetterWithParameterMultiLine' with 'inputData'.
      </button>
    </div>

    <div style="background: red"> newConcatenatedString: {{ newConcatenatedString }} </div>

  </div>
</template>

<script>

  export default {

    data() {
      return {
        someData: 'yo',
        inputData: '',
        newConcatenatedString: ''
      }
    },

    computed: {

      regularComputed(){
        return 'dude.'
      },

      someComputedWithParameterOneLine(){
        return (theParam) => `The following is the Parameter from *One* Line Arrow Function >>> ${theParam}`
      },

      someComputedWithParameterMultiLine(){
        return (theParam) => {
          return `The following is the Parameter from *Multi* Line Arrow Function >>> ${theParam}`
        }
      },

      // NOTICE that Computed with GETTER/SETTER is now an Object, that has 2 methods, get() and set(), so after the name of the computed we use : instead of ()
      // thus we do: "someComputedUsingGetterSetterWithParameterMultiLine: {...}" NOT "someComputedUsingGetterSetterWithParameterMultiLine(){...}"
      someComputedUsingGetterSetterWithParameterMultiLine: {
        get () {
          return (theParam) => {
            return `As part of the computed GETTER/SETTER, the following is inside get() which receives a Parameter (using a multi-line Arrow Function) >>> ${theParam}`
          }
        },
        set(newSetValue) {
          console.log('Accessing get() from within the set()', this.someComputedUsingGetterSetterWithParameterMultiLine('hello from inside the Setter, using the Getter.'))
          console.log('Accessing newSetValue in set() >>>>', JSON.stringify(newSetValue))
          this.newConcatenatedString = `**(1)${this.someComputedUsingGetterSetterWithParameterMultiLine('hello from inside the Setter, using the Getter.')}**  This is a concatenation of get() value that had a Parameter, with newSetValue **(2)${newSetValue}** that came into the set().`
        }
      },

    },

  }

</script>