Vue中的computed与watch的区别
对于模板语法中的复杂逻辑运算,通常我们都会考虑使用计算属性。
计算属性:computed
vue
<template>
<div class="wrapper">
<div>
{{ firstName + ' ' + lastName }}
</div>
<div>{{ fullName }}</div>
</div>
</template>
<script>
export default {
name: 'ComputedVSWatch',
data () {
return {
firstName: 'Foo',
lastName: 'Bar',
}
},
computed: {
fullName () {
const name = this.firstName + ' ' + this.lastName;
return name;
}
},
mounted () {
setTimeout(() => {
this.lastName = 'Baz';
}, 2000)
}
}
</script>
然而,watch也可以完成相应的功能。
侦听器:watch
js
export default {
name: 'ComputedVSWatch',
data () {
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
}
},
watch: {
firstName () {
this.fullName = this.firstName + ' ' + this.lastName;
},
lastName () {
this.fullName = this.firstName + ' ' + this.lastName;
}
},
mounted () {
setTimeout(() => {
this.lastName = 'Baz';
}, 2000)
}
}
同样的功能,使用methods
也可以实现。
方法:methods
vue
<template>
<div class="wrapper">
<div>
Default: {{ firstName + ' ' + lastName }}
</div>
<div>
Methods: {{ getFullName() }}
</div>
</div>
</template>
<script>
export default {
name: 'ComputedVSWatch',
data () {
return {
firstName: 'Foo',
lastName: 'Bar',
}
},
methods: {
getFullName () {
return this.firstName + ' ' + this.lastName;
}
},
mounted () {
setTimeout(() => {
this.lastName = 'Baz';
}, 2000)
}
}
</script>
computed vs methods
vue
<template>
<div class="wrapper">
<div>
<p>Computed:</p>
<p>{{ fullName }}</p>
<p>{{ fullName }}</p>
<p>{{ fullName }}</p>
</div>
<div>
<p>Methods:</p>
<p>{{ getFullName() }}</p>
<p>{{ getFullName() }}</p>
<p>{{ getFullName() }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'ComputedVSWatch',
data () {
return {
firstName: 'Foo',
lastName: 'Bar',
}
},
computed: {
fullName () {
return this.firstName + ' ' + this.lastName;
}
},
methods: {
getFullName () {
return this.firstName + ' ' + this.lastName;
}
},
mounted () {
setTimeout(() => {
this.lastName = 'Baz';
}, 2000)
}
}
</script>
只是看效果的话,两者并没什么区别,那么两者的区别的哪里呢?
尝试在computed
与methods
里面分别添加了一行console.log
看看控制台上打印的是什么?
js
computed: {
fullName () {
console.log('computed执行了一次', this.lastName);
return this.firstName + ' ' + this.lastName;
}
},
methods: {
getFullName () {
console.log('methods执行了一次', this.lastName);
return this.firstName + ' ' + this.lastName;
}
},
plain-text
logs:
--------
computed执行了一次 Bar
methods执行了一次 Bar
methods执行了一次 Bar
methods执行了一次 Bar
computed执行了一次 Baz
methods执行了一次 Baz
methods执行了一次 Baz
methods执行了一次 Baz
我们可以看到,computed
在数据未发生改变之前,无论调用多少次,都只会执行一次其中的代码,也就是说computed
它拥有缓存功能,只有它依赖的数据发生变化时,才会再次运算;而methods
每调用一次就会执行里面的代码。
computed vs watch
vue
<template>
<div class="wrapper">
<div>
<p>Computed:</p>
<p>{{ computedFullName }}</p>
<p>{{ computedFullName }}</p>
<p>{{ computedFullName }}</p>
</div>
<div>
<p>Watch:</p>
<p>{{ watchFullName }}</p>
<p>{{ watchFullName }}</p>
<p>{{ watchFullName }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'ComputedVSWatch',
data () {
return {
firstName: 'Foo',
lastName: 'Bar',
watchFullName: 'Foo Bar'
}
},
computed: {
computedFullName () {
return this.firstName + ' ' + this.lastName;
}
},
watch: {
firstName () {
this.watchFullName = this.firstName + ' ' + this.lastName;
},
lastName () {
this.watchFullName = this.firstName + ' ' + this.lastName;
}
},
mounted () {
setTimeout(() => {
this.lastName = 'Baz';
}, 2000)
}
}
</script>
同样的功能,computed
与watch
都能实现,但是,wacth
需要我们手动地给予初始值。
那么在什么时候使用computed
,什么时候使用watch
呢?
以下这种场景,我们需要异步获取某些数据,并且限制我们执行该操作的频率,并且在我们得到最终结果之前,会出示提示信息。这时候,我们应该考虑使用watch
。
vue
<template>
<div class="wrapper">
<p>
Question: <input type="text" v-model="question">
</p>
<p>
Answer: {{ answer }}
</p>
</div>
</template>
<script>
import _ from 'lodash';
export default {
name: 'ComputedVSWatch',
data () {
return {
question: '',
answer: '请输入问题',
answers: [
{
q: '123',
a: '答案1',
},
{
q: '234',
a: '答案2',
},
{
q: '3456',
a: '答案3'
},
]
}
},
created () {
this.getAnswerDebounce = _.debounce(this.getAnswer, 500);
},
watch: {
question (newQuestion) {
this.answer = '输入完毕之后才会显示结果...';
this.getAnswerDebounce(newQuestion);
}
},
methods: {
getAnswer (question) {
const answers = this.answers,
answer = this.answers.find((item) => item.q == question);
if (answer) {
this.answer = answer.a;
return;
}
this.answer = '问题匹配失败!';
}
}
}
</script>
我们再看一下下面这种场景:
我们需要在用户输入密码1秒后检测密码的正确性,尝试使用computed
。
vue
<template>
<div class="wrapper">
<p>
password: <input type="text" v-model.trim="password">
</p>
<p :style="msgStyle">
{{ message.text }}
</p>
</div>
</template>
<script>
export default {
name: 'ComputedVSWatch',
data () {
return {
password: '',
msg: {}
}
},
computed: {
message () {
let password = this.password,
msg = {
type: 'default',
text: '请输入密码'
};
setTimeout(() => {
if (password.length < 8) {
msg = {
type: 'error',
text: '密码太短了'
};
} else {
msg = {
type: 'success',
text: '密码可以使用'
};
}
}, 1000);
return msg;
},
msgStyle () {
let msg = this.message;
return {
color: msg.type === 'default' ? 'black' : (msg.type === 'error' ? 'red' : 'green')
}
}
}
}
</script>
很显然,使用computed
无法达成我们想要的效果。
尝试使用watch
:
vue
<template>
<div class="wrapper">
<p>
password: <input type="text" v-model.trim="password">
</p>
<p :style="msgStyle">
{{ message.text }}
</p>
</div>
</template>
<script>
export default {
name: 'ComputedVSWatch',
data () {
return {
password: '',
message: {
type: 'default',
text: '请输入密码'
}
}
},
computed: {
msgStyle () {
let msg = this.message;
return {
color: msg.type === 'default' ? 'black' : (msg.type === 'error' ? 'red' : 'green')
}
}
},
watch: {
password () {
setTimeout(() => {
if (this.password.length < 8) {
this.message = {
type: 'error',
text: '密码太短了'
};
} else {
this.message = {
type: 'success',
text: '密码可以使用'
};
}
}, 1000);
}
}
}
</script>
使用watch
,很轻松就可以完成这种效果。
总结:
- 如果一个数据依赖其他数据,那么把这个数据设计为
computed
则更适合; - 如果需要在某个数据发生变化时,做一些事情,使用
watch
则更加适合。
computed
与watch
没有优劣之分,只是使用的场景有所区别,选择合适的无疑会让开发变得更加简单。