method
和computed
在Vue.js中的主要区别是什么?
它们看起来对我来说是相同的并且可以互换使用。
method
和computed
在Vue.js中的主要区别是什么?
它们看起来对我来说是相同的并且可以互换使用。
计算属性和方法在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>
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的文档非常好,易于访问。我推荐它。
由于@gleenk要求提供一个实际的例子来清晰地展示方法和计算属性之间的缓存和依赖差异,我将展示一个简单的场景:
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
值)添加到a
或b
中。关于方法,无论一个特定方法的依赖项是否已更改,它们都会在任何列出的属性上执行每次操作时调用。对于计算属性,只有当依赖项发生更改时才会执行代码;例如,引用A或B的特定属性值之一将分别触发addToAcomputed
或addToBcomputed
。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属性的"相同未更改"的值。在这种情况下,这被认为是良好的实践。
以下是本问题的详细解析:
何时使用方法
何时使用计算属性
计算属性与方法的区别之一是:假设我们有一个函数会返回计数器的值(计数器只是一个变量)。让我们看看在计算属性和方法中函数的行为。
计算属性
在第一次执行函数内部的代码时,Vue.js将在缓存中存储计数器的值(以便更快地访问)。但是,当我们再次调用该函数时,Vue.js不会再次执行该函数内部编写的代码。它首先检查计数器是否有任何更改。如果有任何更改,它才会重新执行函数内部的代码。如果计数器没有发生更改,Vue.js将不会重新执行该函数,并且仅简单地从缓存中返回先前的结果。
方法
这就像JavaScript中的普通方法。每当我们调用该方法时,它都会始终执行函数内部的代码,而不管计数器是否发生了更改。
方法总是无论代码是否更改都会重新执行代码。而计算属性只有在其依赖项的值更改时才会重新执行代码。否则,它将从缓存中返回上一个结果而不重新执行代码。
计算属性
计算属性也被称为计算值。这意味着它们可以随时更新和更改。同时,它会缓存数据直到数据发生变化。当Vue被实例化时,计算属性被转换为一个属性。
还有一件事要分享,您不能在计算属性中传递任何参数,因此在调用任何计算属性时不需要使用括号。
方法
方法与函数相同并且工作方式也相同。此外,除非您调用它,否则该方法不执行任何操作。而且,像所有JavaScript函数一样,它接受参数,并且每次调用时都会重新计算。之后,它们无法缓存值。
在调用方法时需要使用括号,并且您可以发送一个或多个参数。
我也遇到了同样的问题。对我来说,更清晰的表述是:
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.*/
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
属性作为最佳实践。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 和原生 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
}
}
区别:
计算属性:
方法:
我会尝试补充其他成员的答案。这个例子和解释使我完全理解了计算属性的要点。我希望在阅读我的帖子后,你也能意识到它。
如果你需要改变数据,你必须使用方法。当你需要改变现有数据的呈现方式时,你将使用计算属性。随着你练习这两个概念,你会轻松地使用它们。以下是一些关键点:
正如你已经阅读或运行了我的示例代码后,你会发现只有在计算属性中呈现的值被改变(无论是在方法内部、用户输入还是其他方式),计算属性将被重新计算并缓存。 但每次调用方法时,它都将被执行,而不考虑结果(例如,在我的示例中,当一个值达到 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>