Vue.js中方法和计算值的主要区别是什么?

在我看来,它们是一样的,是可以互换的。


当前回答

根据vueJs文档,一个简单的方法是:

相比之下,每当重新呈现发生时,方法调用将始终运行该函数。

而计算属性只有在其响应性依赖项发生更改时才会重新计算

其他回答

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

当Vue.js看到v-on指令后面跟着一个方法时,它确切地知道要调用哪个方法以及何时调用它。

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

当一个方法在没有v-on指令的情况下被调用时,它将在更新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.*/

Computed属性仅在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.  */

这里的要点是,在没有使用v-on指令调用方法的情况下使用computed属性是最佳实践。

Vue中的计算值和方法非常不同,在大多数情况下肯定是不可互换的。

计算属性

A more appropriate name for a computed value is a computed property. In fact, when the Vue is instantiated, computed properties are converted into a property of the Vue with a getter and sometimes a setter. Basically you can think of a computed value as a derived value that will be automatically updated whenever one of the underlying values used to calculate it is updated. You don't call a computed and it doesn't accept any parameters. You reference a computed property just like you would a data property. Here's the classic example from the 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的文档非常好,而且易于访问。我推荐它。

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

例子:

计算:

它是一个默认接受getter回调作为参数的函数,并根据其他属性(如ref, reactive或store state)返回一个不可变的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属性观看它

方法:

用于重构computed/watcher属性或其他函数中的代码 用作事件处理程序 为了避免呈现问题,不应该在模板中调用它。

computed和methods之间的区别在于,当你在computed中定义一个函数时,它只在答案改变时才从头执行该函数,而methods在每次调用时都从头执行该函数。

我会尽量补充其他成员的回答。这个例子和解释让我完全掌握了计算属性的要点。我希望在看完我的文章后,你也能意识到这一点。


如果需要更改数据,则必须使用方法。当您需要更改现有数据的表示时,您将使用计算属性。当您练习这两个概念时,您将轻松地使用它们。以下是一些奇特的钥匙:

计算属性必须总是返回一个值; 计算属性仅用于转换数据,而不是用于改变我们的表示层|,它们不应该改变或改变现有的数据。

由于您已经阅读了它或在运行我的示例代码之后,您将看到只有在计算属性中显示的值被更改(在方法中或通过用户输入或其他方式),计算属性将被重新计算并缓存。 但是每次调用一个方法时,不管结果如何,它都会被执行(例如,在我的例子中,当一个值达到0时,计算的属性不再被重新计算)

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

自己的现金; 你银行账户里的现金; 可从您的银行帐户提款; 有可能从某人那里借一些钱(无限的钱)。

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>