【问题标题】:Methods vs Computed in VueVue 中的方法与计算
【发布时间】:2017-11-05 03:50:04
【问题描述】:

Vue.js 中methodscomputed 值之间的主要区别是什么?

它们看起来相同且可以互换。

【问题讨论】:

标签: javascript methods vue.js vuejs2 computed-properties


【解决方案1】:

Vue 中的计算值和方法非常不同,在大多数情况下绝对不能互换。

计算属性

计算值更合适的名称是computed property。事实上,当 Vue 被实例化时,计算属性会被转换为 Vue 的属性,其中包含一个 getter,有时还有一个 setter。基本上,您可以将计算值视为派生值,只要用于计算它的基础值之一被更新,它就会自动更新。您不调用计算并且它不接受任何参数。就像引用数据属性一样引用计算属性。这是来自documentation 的经典示例:

computed: {
  // a computed getter
  reversedMessage: function () {
    // `this` points to the vm instance
    return this.message.split('').reverse().join('')
  }
}

在 DOM 中是这样引用的:

<p>Computed reversed message: "{{ reversedMessage }}"</p>

计算值对于处理 Vue 中存在的数据非常有价值。每当您想要过滤或转换数据时,通常都会为此目的使用计算值。

data:{
    names: ["Bob", "Billy", "Mary", "Jane"]
},
computed:{
    startsWithB(){
        return this.names.filter(n => n.startsWith("B"))
    }
}

<p v-for="name in startsWithB">{{name}}</p>

计算的值也会被缓存,以避免重复计算一个在未更改时不需要重新计算的值(例如,它可能不在循环中)。

方法

方法只是绑定到 Vue 实例的函数。只有当您明确调用它时才会对其进行评估。像所有 javascript 函数一样,它接受参数并且每次调用时都会重新计算。方法在相同的情况下有用,任何函数都有用。

data:{
    names: ["Bob", "Billy", "Mary", "Jane"]
},
computed:{
    startsWithB(){
        return this.startsWithChar("B")
    },
    startsWithM(){
        return this.startsWithChar("M")
    }
},
methods:{
    startsWithChar(whichChar){
        return this.names.filter(n => n.startsWith(whichChar))
    }
}

Vue 的 documentation 非常好并且易​​于访问。我推荐它。

【讨论】:

【解决方案2】:

@gleenk 要求提供一个实际示例来说明方法和计算属性之间的缓存和依赖关系差异,我将展示一个简单的场景:

app.js

new Vue({
    el: '#vue-app',
    data: {
        a: 0,
        b: 0,
        age: 20
    },
    methods: {
        addToAmethod: function(){
            console.log('addToAmethod');
            return this.a + this.age;
        },
        addToBmethod: function(){
            console.log('addToBmethod');
            return this.b + this.age;
        }
    },
    computed: {
        addToAcomputed: function(){
            console.log('addToAcomputed');
            return this.a + this.age;
        },
        addToBcomputed: function(){
            console.log('addToBcomputed');
            return this.b + this.age;
        }
    }
});

这里我们有 2 个方法和 2 个计算属性来执行相同的任务。方法addToAmethod & addToBmethod 和计算属性addToAcomputed & addToBcomputed 都将+20(即age 值)添加到ab。关于这些方法,它们每次对列出的属性中的任何执行操作时被调用,即使某个特定属性的依赖项方法没有改变。对于计算属性,仅当依赖项发生更改时才执行代码;例如,引用 A 或 B 的特定属性值之一将分别触发 addToAcomputedaddToBcomputed

方法和计算描述看起来很相似,但正如@Abdullah Khan 已经specified 它,它们不是一回事!现在让我们尝试添加一些 html 来一起执行所有内容,看看有什么不同。

方法案例演示

new Vue({
    el: '#vue-app',
    data: {
        a: 0,
        b: 0,
        age: 20
    },
    methods: {
        addToAmethod: function(){
            console.log('addToAmethod');
            return this.a + this.age;
        },
        addToBmethod: function(){
            console.log('addToBmethod');
            return this.b + this.age;
        }
    }
});
<!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>VueJS Methods - stackoverflow</title>
            <link href="style.css" rel="stylesheet" />
            <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.min.js"></script>
    
        </head>
        <body>
            <div id="vue-app">
                <h1>Methods</h1>
                <button v-on:click="a++">Add to A</button>
                <button v-on:click="b++">Add to B</button>
                <p>Age + A = {{ addToAmethod() }}</p>
                <p>Age + B = {{ addToBmethod() }}</p>
            </div>
        </body>
        
        <script src="app.js"></script>
    </html>

解释结果

当我点击按钮“添加到A”时,所有方法都被调用(见上面的控制台日志屏幕结果),addToBmethod()也被执行但我没有按下“添加到 B” 按钮;引用 B 的属性值没有改变。如果我们决定单击按钮“添加到 B”,也会出现相同的行为,因为这两个方法都将独立于依赖项更改而被调用。根据这种情况,这是不好的做法,因为我们每次都在执行方法,即使依赖项没有改变。这真的很消耗资源,因为没有缓存未更改的属性值。

计算属性案例演示

new Vue({
    el: '#vue-app',
    data: {
        a: 0,
        b: 0,
        age: 20
    },

    computed: {
        addToAcomputed: function(){
            console.log('addToAcomputed');
            return this.a + this.age;
        },
        addToBcomputed: function(){
            console.log('addToBcomputed');
            return this.b + this.age;
        }
    }
});
<!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>VueJS Computed properties - stackoverflow</title>
            <link href="style.css" rel="stylesheet" />
            <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.11/vue.min.js"></script>
        </head>
        <body>
            <div id="vue-app">
                <h1>Computed Properties</h1>
                <button v-on:click="a++">Add to A</button>
                <button v-on:click="b++">Add to B</button>
                <p>Age + A = {{ addToAcomputed }}</p>
                <p>Age + B = {{ addToBcomputed }}</p>
            </div>
        </body>
        
        <script src="app.js"></script>
    </html>

解释结果

当我点击按钮“添加到A”时,只会调用计算属性addToAcomputed,因为正如我们已经说过的,计算属性只有在依赖项发生变化时才会执行。而且由于我没有按下按钮“添加到 B” 并且 B 的年龄属性值没有改变,所以没有理由调用和执行计算属性 addToBcomputed。因此,在某种意义上,计算属性为 B 属性维护“相同不变”的值,就像一种缓存一样。在这种情况下,这是一种良好做法

【讨论】:

  • 为什么在按下 1 个按钮时会执行所有方法?原因/逻辑是什么?
  • @Bsienn 这是一个很好的问题:原因是 Vue 基本上不知道根据更新的内容需要运行哪一种方法。这就是计算属性所做的操作,它们监视需要计算或重新计算的变量,并且只在需要时运行。
  • 以及使用方法的原因是什么?看起来计算属性更好(假设我们正在谈论'get'方法)......
  • @user3529607 但计算属性不接收参数。
  • @user3529607 据我了解,方法在挂载或创建 vue 实例时很有用。计算属性也无法做到这一点。此外,我们必须返回计算属性的值。
【解决方案3】:

这里是这个问题的细分。

何时使用方法

  • 对 DOM 中发生的某些事件做出反应
  • 在组件中发生某些事情时调用函数。
  • 您可以从计算属性或观察者调用方法。

何时使用计算属性

  • 您需要从现有数据源组合新数据
  • 您在模板中使用了一个由一个或多个数据属性构建的变量
  • 您希望将复杂的嵌套属性名称简化为更易读、更易于使用的名称(但在原始属性更改时更新它)
  • 您需要从模板中引用一个值。在这种情况下,最好创建一个计算属性,因为它是缓存的。
  • 您需要监听多个数据属性的变化

【讨论】:

  • 明确的最佳答案。谢谢迭戈
  • 非常清楚,我在寻找什么。大多数答案都解释了为什么计算值很好,但我知道这一点。我实际上是在寻找如果计算非常好,为什么你甚至想要使用方法。这至少解释了其中的一部分。
【解决方案4】:

来自docs

..computed 属性根据它们的依赖关系进行缓存。计算属性仅在其某些依赖项发生更改时才会重新评估。

如果您希望缓存数据,请使用计算属性,如果您不希望缓存数据,请使用简单的方法属性。

【讨论】:

  • 嗨,你能写一个有用的例子来展示实际使用的差异吗?
  • @gleenk 我将添加一个实际示例,向您展示方法和计算属性之间的缓存/依赖关系差异。我希望你会喜欢它。
  • 谢谢@GiulioBambini
【解决方案5】:

计算和方法之间的区别之一。假设我们有一个返回计数器值的函数。(计数器只是变量)。让我们看看函数在 computedmethod

中的行为

计算

在第一次执行时,函数内部的代码将被执行,vuejs 会将计数器值存储在缓存中(以便更快地访问)。但是当我们再次调用该函数时,vuejs 将不会再次执行该函数内部编写的代码。它首先检查对计数器所做的任何更改。如果进行了任何更改,那么只有它会重新执行该函数内的代码。如果没有对计数器进行任何更改,vuejs 将不会再次执行该函数。它只会从缓存中返回之前的结果。

方法

这就像 javascript 中的普通方法一样。每当我们调用该方法时,它总是会执行函数内部的代码,而不管对计数器所做的更改。

方法总是会重新执行代码,而不管代码的变化。 where as computed 将仅在其中一个依赖项的值发生更改时重新执行代码。否则它会从缓存中给我们之前的结果而不重新执行

【讨论】:

    【解决方案6】:

    计算属性

    计算属性也称为计算值。这意味着,它们会更新并且可以随时更改。此外,它会缓存数据,直到它发生变化。当 Vue 被实例化时,计算属性被转换为属性。

    我还想分享一件事,你不能在计算属性中传递任何参数,这就是为什么在调用任何计算机属性时不需要括号。

    方法

    方法与函数相同,工作方式相同。此外,除非你调用一个方法,否则它什么也不做。此外,与所有 javascript 函数一样,它接受参数,并且每次调用时都会重新计算。之后,他们就不能缓存值了

    在调用括号的方法中,您可以在其中发送一个或多个参数。

    【讨论】:

    • 那么你是说计算值是在初始化时计算的,而方法只在调用时计算?
    【解决方案7】:

    偶然发现了同样的问题。对我来说,这样更清楚:

    1. 当 Vue.js 看到 v-on directive 后跟一个方法时,它会准确地知道 哪个方法 调用以及 何时 调用它.
    <button v-on:click="clearMessage">Clear message</button> // @click
    // method clearMessage is only called on a click on this button
    
    <input v-model="message" @keyup.esc="clearMessage" @keyup.enter="alertMessage" />
    /* The method clearMessage is only called on pressing the escape key
    and the alertMessage method on pressing the enter key */
    
    1. 当一个方法被调用没有v-on directive时,它会在页面上被调用每次触发事件更新DOM(或者只是需要重新-渲染页面的一部分)。即使该方法与被触发的事件无关。
    <p>Uppercase message: {{ messageUppercase() }}</p>
    methods: {
       messageUppercase() {
          console.log("messageUpercase");
          return this.message.toUpperCase();
       }
    }
    /* The method `messageUppercase()` is called on every button click, mouse hover 
    or other event that is defined on the page with the `v-on directive`. So every
    time the page re-renders.*/
    
    1. 只有在函数定义中的 this 引用的属性值发生更改时,才会调用计算属性。
    <p>Uppercase message: {{ messageUppercase }}</p> 
    data() {
     return {
        message: "I love Vue.js"
       }
     },
    computed: {
     messageUppercase() {
        console.log("messageUpercase");
        return this.message.toUpperCase();
     }
    }
    /* The computed property messageUppercase is only called when the propery message is
    changed. Not on other events (clicks, mouse hovers,..) unless of course a specific 
    event changes the value of message.  */
    

    这里的要点是,最好使用computed 属性,以防不使用v-on directive 调用方法。

    【讨论】:

      【解决方案8】:

      按照 vueJs 文档的简单方法:

      相比之下,只要发生重新渲染,方法调用将始终运行函数。

      虽然计算属性只会在其某些反应性依赖项发生变化时重新评估

      【讨论】:

        【解决方案9】:

        在 Vue 3 附带的可作为 vue 2 插件使用的 vue composition API 中,方法和计算属性是不同的语法:

        示例:

        计算:

        它是一个函数,默认情况下将 getter 回调作为参数,并根据 ref、reactive 或 store 状态等其他属性返回不可变的 ref。

        import {computed,ref} from 'vue'
        
        export default{
        
        setup(){
          const count=ref(0);
          
          const doubleCount=computed(()=>count.value*2) 
        
         return {count,doubleCount} //expose the properties to the template 
         }
        }
        

        方法

        是普通的 javascript 函数,在 Vue 和 vanilla js 中的行为方式相同,它们暴露于模板并用作事件处理程序,它们不应该用于渲染目的,这可能会导致一些问题,如无限渲染。

        import {computed,ref} from 'vue'
        
        export default{
        
        setup(){
          const count=ref(0);
          
          const doubleCount=computed(()=>count.value*2) 
         
          function increment(){
           ref.value++
         }
        
         return {count,doubleCount,increment} //expose the properties/functions to the template 
         }
        }
        

        区别:

        计算:

        • 它被评估为不可变属性而不是函数
        • 它观察另一个属性并基于该属性返回一个属性。
        • 不能带参数。
        • 可以使用 watch 属性进行观看

        方法:

        • 用于重构计算/观察器属性或其他函数中的代码
        • 用作事件处理程序
        • 不应在模板内调用它以避免呈现问题。

        【讨论】:

          【解决方案10】:

          我会尝试补充其他成员的答案。这个例子和解释让我完全理解了计算属性的要点。我希望在阅读我的帖子后,你也会意识到这一点。


          如果你需要改变数据,你必须使用方法。当您需要更改现有数据的表示时,您将使用计算属性。当您练习这两个概念时,您将轻松使用它们。以下是一些常用的关键字:

          1. 计算属性必须始终返回一个值;
          2. 计算属性仅用于转换数据,而不用于为我们的表示层更改数据 |他们不应更改或更改现有数据。

          正如您已经阅读或运行我的示例代码后,您将看到只有计算属性中显示的值发生了变化(在方法内部或通过用户输入或通过其他方式),计算属性将被重新计算和缓存。 但是每次调用方法时,无论结果如何都会执行(例如,在我的示例中,当值达到 0 值时,不再重新计算计算的属性)

          在例子中,有一个简单的系统;你在哪里:

          • 拥有现金;
          • 您在银行账户中的现金;
          • 可以从您的银行账户中提款;
          • 可以从某个人那里借一些钱(用 Infinity 钱)。

          new Vue({
            el: '#app',
            data: {
              infinity: Infinity,
              value: 3,
              debt: -6,
              cash: 9,
              moneyInBank: 15,
            },
          
            computed: {
              computedPropRemainingCashFundsIfPaid: function() {
                console.log('computedPropRemainingCashFundsIfPaid');
                return this.debt + this.cash;
              },
              computedPropRemainingTotalFunds: function() {
                console.log('computedPropRemainingTotalFunds');
                return this.cash + this.moneyInBank + this.debt;
              }
            },
            methods: {
              depositFunds: function(from, to, value, limit = false) {
                if (limit && (this[to] + value) >= 0) { // if you try to return greater value than you owe
                  this[from] += this[to];
                  this[to] = 0;
                } else if (this[from] > value && this[from] - value >= 0) { // usual deposit
                  this[to] += value;
                  this[from] -= value;
                } else { // attempt to depost more than you have
                  this[to] += this[from];
                  this[from] = 0;
                }
              },
              repayADebt: function() {
                this.value = Math.abs(this.value);
                if (this.debt < 0) {
                  this.depositFunds('cash', 'debt', this.value, true);
                }
                console.log('Attempt to repayADebt', this.value);
              },
              lendAmount: function() {
                this.depositFunds('infinity', 'debt', -Math.abs(this.value));
                console.log('Attempt to lendAmount', this.value);
              },
              withdraw: function() {
                if (this.moneyInBank) {
                  this.depositFunds('moneyInBank', 'cash', this.value);
                }
                console.log('Attempt to withdraw', this.value);
              }
            }
          });
          * {
            box-sizing: border-box;
            padding: 0;
            margin: 0;
            overflow-wrap: break-word;
          }
          
          html {
            font-family: "Segoe UI", Tahoma, Geneva, Verdana;
            font-size: 62.5%;
          }
          
          body {
            margin: 0;
            font-size: 1.6rem;
          }
          
          #app {
            margin: 3rem auto;
            max-width: 50vw;
            padding: 1rem;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
          }
          
          label,
          input {
            margin-bottom: 0.5rem;
            display: block;
            width: 100%;
          }
          
          label {
            font-weight: bold;
          }
          
          ul {
            list-style: none;
            margin: 1rem 0;
            padding: 0;
          }
          
          li {
            margin: 1rem 0;
            padding: 1rem;
            border: 1px solid #ccc;
          }
          
          .grid {
            display: grid;
            grid: 1fr / 1fr min-content 1fr min-content;
            gap: 1rem;
            align-items: center;
            margin-bottom: 1rem;
          }
          
          .grid> :is(button, input) {
            height: 3rem;
            margin: 0;
          }
          
          .computed-property-desc {
            padding: 1rem;
            background-color: rgba(0, 0, 0, 0.3);
            text-align: justify;
          }
          <!DOCTYPE html>
          <html lang="en">
          
          <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>A First App</title>
            <link rel="stylesheet" href="styles.css" />
          </head>
          
          <body>
            <div id="app">
          
              <h1>Computed Properties Guide</h1>
              <p style="background-color: bisque;">
                Let's assume that you have <span v-once>{{ cash }}</span>$; And you need to pay a debt=<span v-once>{{ debt }}</span>
              </p>
              <p>Your bank account: {{ moneyInBank }}$ <button v-on:click="withdraw(value)">Withdrow {{ value }}$ from
                          bank</button></p>
              <p>Your cash: {{ cash }}$</p>
              <p>Your debt: {{ debt }}$ <button v-on:click="lendAmount(value)">Lend {{ value }}$ from Infinity</button></p>
              <div class="grid">
                <button v-on:click="repayADebt(value)">Repay a debt</button>
                <span>in amout of</span>
                <input type="text" v-model.number="value">
                <span>$</span>
              </div>
          
              <p>computedPropRemainingCashFundsIfPaid/<br><mark>Available funds in case of debt repayment</mark> = {{ computedPropRemainingCashFundsIfPaid }}$</p>
              <p>computedPropRemainingTotalFunds = {{ computedPropRemainingTotalFunds }}$</p>
          
              <p class="computed-property-desc">when you need to change data, you will use methods. And When you need to change the presentation of existing data, you will use computed properties. As you practice both concepts, it will become easier which one should you use. Very important notes:
                1. it must always return a value; 2. computed properties are only used for transforming data and not for chaning it for our presentation layer | they should not alter or change the existing data</p>
          
            </div>
          
            <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
          </body>
          
          </html>

          【讨论】:

            猜你喜欢
            • 2018-02-18
            • 2019-09-02
            • 2021-07-01
            • 2022-01-04
            • 2018-03-23
            • 1970-01-01
            • 2020-10-08
            • 2019-01-27
            • 2020-12-01
            相关资源
            最近更新 更多