【问题标题】:Can I pass parameters in computed properties in Vue.Js我可以在 Vue.Js 的计算属性中传递参数吗
【发布时间】:2018-03-18 09:56:07
【问题描述】:

这是否可以在 Vue.Js 的计算属性中传递参数。我可以看到当 getter/setter 使用计算时,他们可以接受一个参数并将其分配给一个变量。喜欢这里来自documentation:

// ...
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    
  }
}
// ...

其中计算属性接受一个参数并返回所需的输出。但是,当我尝试这个时,我收到了这个错误:

vue.common.js:2250 Uncaught TypeError: fullName is not a function(…)

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

【问题讨论】:

  • 不,您不能将参数传递给计算属性。是的,使用方法是最简单的方法。

标签: javascript vue.js vuejs2


【解决方案1】:

计算可以被认为是一个函数。因此,对于validation 的示例,您显然可以执行以下操作:

    methods: {
        validation(attr){
            switch(attr) {
                case 'email':
                    const re = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
                    return re.test(this.form.email);
                case 'password':
                    return this.form.password.length > 4
            }
        },
        ...
    }

您将使用如下:

  <b-form-input
            id="email"
            v-model="form.email"
            type="email"
            :state="validation('email')"
            required
            placeholder="Enter email"
    ></b-form-input>

请记住,您仍然会错过特定于 computed 的缓存。

【讨论】:

    【解决方案2】:

    我想首先重申之前的警告,即使用带参数的计算(已缓存)只会使计算不被缓存,实际上只是使其成为一种方法。

    但是,话虽如此,这里是我能想到的所有变体,它们可能有边缘情况可供使用。如果您将其剪切并粘贴到演示应用程序中,则应该清楚发生了什么:

    <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>
    

    【讨论】:

      【解决方案3】:

      很可能你想使用一种方法

      <span>{{ fullName('Hi') }}</span>
      
      methods: {
        fullName(salut) {
            return `${salut} ${this.firstName} ${this.lastName}`
        }
      }
      

      更长的解释

      从技术上讲,您可以使用带有如下参数的计算属性:

      computed: {
         fullName() {
            return salut => `${salut} ${this.firstName} ${this.lastName}`
         }
      }
      

      (感谢Unirgy为此提供基本代码。)

      计算属性和方法之间的区别在于计算属性被缓存,并且只有在它们的依赖关系发生变化时才会发生变化。 方法会在每次调用时进行评估。

      如果您需要参数,在这种情况下使用计算属性函数通常没有任何好处。尽管它允许您将参数化的 getter 函数绑定到 Vue 实例,但您会丢失缓存,因此实​​际上并没有任何收益,实际上,您可能会破坏反应性 (AFAIU)。您可以在 Vue 文档 https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods

      中阅读更多相关信息

      唯一有用的情况是您必须使用 getter 并需要对其进行参数化。例如,这种情况发生在 Vuex 中。在 Vuex 中,这是从存储中同步获取参数化结果的唯一方法(操作是异步的)。因此,Vuex 官方文档为它的 getter 列出了这种方法 https://vuex.vuejs.org/guide/getters.html#method-style-access

      【讨论】:

      • 非常感谢!
      【解决方案4】:

      过滤器是 Vue 组件提供的一项功能,可让您将格式设置和转换应用于模板动态数据的任何部分。

      它们不会改变组件的数据或任何东西,但只会影响输出。

      假设你正在打印一个名字:

      new Vue({
        el: '#container',
        data() {
          return {
            name: 'Maria',
            lastname: 'Silva'
          }
        },
        filters: {
          prepend: (name, lastname, prefix) => {
            return `${prefix} ${name} ${lastname}`
          }
        }
      });
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
      <div id="container">
        <p>{{ name, lastname | prepend('Hello') }}!</p>
      </div>

      注意应用过滤器的语法,即 |过滤器名称。如果你熟悉 Unix,那就是 Unix 管道操作符,它用于将操作的输出作为输入传递给下一个操作。

      组件的过滤器属性是一个对象。 单个过滤器是一个接受一个值并返回另一个值的函数。

      返回值是实际打印在 Vue.js 模板中的值。

      【讨论】:

        【解决方案5】:
        computed: {
          fullName: (app)=> (salut)=> {
              return salut + ' ' + this.firstName + ' ' + this.lastName    
          }
        }
        

        当你想使用时

        <p>{{fullName('your salut')}}</p>
        

        【讨论】:

          【解决方案6】:

          您还可以通过返回函数将参数传递给 getter。这在您想查询存储中的数组时特别有用:

          getters: {
            // ...
            getTodoById: (state) => (id) => {
              return state.todos.find(todo => todo.id === id)
            }
          }
          store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
          

          请注意,通过方法访问的 getter 将在您每次调用它们时运行,并且结果不会被缓存。

          这称为方法样式访问,它是is documented on the Vue.js docs。

          【讨论】:

            【解决方案7】:

            你可以传递参数,但要么不是 vue.js 的方式,要么是你做的方式不对。

            但是在某些情况下您需要这样做。我将向您展示一个使用 getter 和 setter 将值传递给计算属性的简单示例。

            <template>
                <div>
                    Your name is {{get_name}} <!-- John Doe at the beginning -->
                    <button @click="name = 'Roland'">Change it</button>
                </div>
            </template>
            

            还有剧本

            export default {
                data: () => ({
                    name: 'John Doe'
                }),
                computed:{
                    get_name: {
                        get () {
                            return this.name
                        },
                        set (new_name) {
                            this.name = new_name
                        }
                    },
                }    
            }
            

            单击按钮时,我们将名称“Roland”传递给计算属性,在set() 中,我们将名称从“John Doe”更改为“Roland”。

            以下是计算与 getter 和 setter 一起使用时的常见用例。 假设您有以下 vuex 商店:

            export default new Vuex.Store({
              state: {
                name: 'John Doe'
              },
              getters: {
                get_name: state => state.name
              },
              mutations: {
                set_name: (state, payload) => state.name = payload
              },
            })
            

            在您的组件中,您想将v-model 添加到输入但使用 vuex 存储。

            <template>
                <div>
                    <input type="text" v-model="get_name">
                    {{get_name}}
                </div>
            </template>
            <script>
            export default {
                computed:{
                    get_name: {
                        get () {
                            return this.$store.getters.get_name
                        },
                        set (new_name) {
                            this.$store.commit('set_name', new_name)
                        }
                    },
                }    
            }
            </script>
            

            【讨论】:

              【解决方案8】:

              好吧,从技术上讲,我们可以将参数传递给计算函数,就像我们可以将参数传递给 vuex 中的 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 的项目在商店中发生变化时,视图会使用该项目的新属性自动刷新其内容(绑定似乎工作得很好)。

              【讨论】:

              • 哇,所以这对我有用,而不是使用 vuex。还想知道这是否是计算属性的合法方式。
              • 虽然这确实有效,但它本质上将计算属性视为方法。即它失去了计算属性的缓存优势。因此,使用此方法没有实际收益。 “请注意,通过方法访问的 getter 将在您每次调用它们时运行,并且结果不会被缓存。”见vuex.vuejs.org/en/getters.html
              • @james.brndwgn 但我很确定当基础数据发生变化时方法不会重新运行。这就是我真正想要的。
              • @Alex 那么你应该使用观察者。 vuejs.org/v2/guide/computed.html#Watchers
              • @james.brndwgn 如果可能的话,我宁愿使用计算属性而不是观察者。我只是对您的陈述提出异议:“因此,使用这种方法并没有实际收益。”因为即使没有缓存也存在显着差异。
              【解决方案9】:

              是的,有使用参数的方法。与上述答案一样,在您的示例中,最好使用方法,因为执行非常轻松。

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

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

              注意:使用这个时,如果处理数千个,请注意内存

              【讨论】:

                【解决方案10】:

                您可以使用方法,但我更喜欢使用计算属性而不是方法,如果它们不会改变数据或没有外部影响。

                您可以通过这种方式将参数传递给计算属性(未记录,但维护人员建议,不记得在哪里):

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

                编辑:请不要使用此解决方案,它只会使代码复杂化而没有任何好处。

                【讨论】:

                • 如果您能提供参考将非常有帮助。这应该有效。
                • @saurabh 抱歉,这是 github 中一个非描述性问题的解决方案,我现在找不到它......
                • 这对我有用,但我唯一不喜欢的是它返回一个函数而不是实际属性,因此 VueJS 开发工具不会在任何地方显示结果。我不确定这是否是计算属性的典型情况,但这会使故障排除有点困难。
                • 它如何处理缓存?参数变化时会正常工作吗?
                • 我不相信它会在返回函数中缓存任何东西。与方法的区别纯粹是约定(方法有效,计算仅用于检索)
                猜你喜欢
                • 1970-01-01
                • 2021-03-23
                • 2015-10-26
                • 2017-09-19
                • 2017-12-31
                • 1970-01-01
                • 2020-07-28
                • 2020-04-23
                相关资源
                最近更新 更多