方法vs Vue计算

Vue.js中methodcomputed值的主要区别是什么?

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

136792 次浏览

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

计算属性

更合适的计算值名称是计算属性。事实上,在实例化Vue时,计算属性将使用getter(有时是setter)转换为Vue的属性。基本上,您可以将计算值视为派生值,每当用于计算它的底层值之一更新时,该派生值将自动更新。你不计算调用,它不接受任何参数。引用计算属性就像引用数据属性一样。下面是来自文档的经典示例:

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的文档真的很好,而且很容易访问。我推荐它。

docs

..计算属性是基于它们的依赖项缓存的。计算属性只有在其某些依赖项发生更改时才会重新计算。

如果您希望缓存数据,请使用Computed属性,另一方面,如果您不想缓存数据,请使用简单的Method属性。

@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;
}
}
});

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

方法和计算描述似乎非常相似,但@Abdullah Khan已经指定的它,它们不是一回事!现在让我们尝试添加一些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>

解释结果

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

method button method

.

Computed属性案例演示

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>

解释结果

当我单击按钮“加入”;时,只有计算属性addToAcomputed被调用,因为,正如我们已经说过的,计算属性只有在依赖项发生变化时才会执行。因为我没有按下按钮“加入”;, B的age属性值也没有改变,所以没有理由调用并执行计算属性addToBcomputed。因此,在某种意义上,计算属性是保持“不变”的;B属性的值,就像一种缓存。在这种情况下,考虑良好的实践

computed button computed

.

计算属性

计算属性也称为计算值。这意味着,它们可以随时更新和更改。此外,它还缓存数据,直到数据发生更改。在实例化Vue时,计算属性将转换为属性。

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

方法

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

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

计算与方法的区别之一。假设我们有一个返回计数器值的函数。(counter只是变量)。让我们看看函数在计算方法中的行为

计算

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

方法

这就像javascript中的一个普通方法。无论何时调用该方法,它都将始终执行函数内部的代码,而不管对计数器做了什么更改。

方法将始终重新执行代码,而不考虑代码中的更改。其中as computed将重新执行代码,然后只有当它的一个依赖项的值改变了。否则,它将在不重新执行的情况下,向我们提供之前缓存的结果

下面是这个问题的详细分析。

何时使用方法

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

何时使用计算属性

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

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

  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在其函数定义中引用的属性值被更改时,Computed属性为只叫
<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 directive调用方法的情况下,使用computed属性是最佳实践。

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

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

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

在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属性或其他函数中的代码
  • 用作事件处理程序
  • 为了避免呈现问题,不应该在模板中调用它。

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


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

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

因为你已经读过它或在你运行我的示例代码之后,你会看到只有在计算属性中显示的值被bieng改变了(无论是在方法中还是通过用户输入或其他方式),计算属性将被重新计算和缓存。 但是每次调用一个方法时,不管结果如何,它都将被执行(例如,在我的例子中,当一个值达到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>

下面是Vue3文档说的——看一个例子:

对于最终结果,这两种方法确实完全相同。然而,不同之处在于计算属性基于它们的响应性依赖项进行缓存。计算属性只有在其响应性依赖项发生更改时才会重新计算。[…相比之下,方法调用将在重新呈现发生时总是运行该函数。

额外的链接

  1. 方法
  2. Computed Properties .

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