计算属性 computed
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
对于任何包含响应式数据(会根据用户的一些行为而变化的数据)的复杂逻辑,你都应该使用计算属性
计算属性默认提供一个get
方法。
计算属性使用示例
那么计算属性应该怎么使用呢?
请看例子:
如果一个用户的书单列表的长度大于0,就显示“有书”,如果长度=0,就显示“没书”。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>计算属性</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<div id="username">{{name}}</div>
<div id="info">
<span>书单中是否有书:</span>
<span>{{ bookListMessage }}</span>
</div>
</div>
</body>
<script>
const app = {
data() {
return {
name: "肉丝儿",
bookList: [
"爱丽丝梦游仙境",
"山海经",
]
}
},
computed: {
bookListMessage() {
return this.bookList.length > 0 ? "有书" : "无书";
}
}
}
Vue.createApp(app).mount("#app")
</script>
</html>
你可以像普通属性一样将数据绑定到模板中的计算属性。
当计算属性中依赖的数据发生改变时,所有依赖到这个数据的计算属性也会更新。
计算属性和方法有些相似,那么他们有什么区别呢?
- 计算属性:计算属性是基于依赖关系进行缓存的,计算属性只在相关响应式依赖发生改变时它们才会重新求值。
- 方法:方法每次只要调用,都会被执行,执行其中完整的运算过程。
为什么需要缓存?假设我们有一个性能开销比较大的计算属性 list
,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 list
。如果没有缓存,我们将不可避免的多次执行 list !如果你不希望使用缓存,请用方法来替代。
计算属性的setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>计算属性</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<div @click="changeFileName()">{{ fileName }}</div>
</div>
</body>
<script>
const app = {
data() {
return {
n: "刘德华",
}
},
methods: {
changeFileName() {
this.fileName = "吴彦祖";
}
},
computed: {
fileName: {
get() {
console.log("调用了get方法")
return this.n;
},
set(newName) {
console.log(newName)
this.n = newName
}
}
},
}
Vue.createApp(app).mount("#app")
</script>
</html>
在图中可以看到,当我第一次点击名字的时候,调用了计算属性的set()
。然后调用了get()
,当我在次点击名字的时候,又调用了set()
,但是此时因为值没有发生变化,所以没有调用get()
,这也可以看出了,计算属性是有缓存的,依赖的数据没有发生变化时,不会调用get()
。
侦听器 watch
当需要在数据变化时执行异步或开销较大的操作时,侦听器方式是最有用的。
html部分
<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question" />
</p>
<p>{{ answer }}</p>
</div>
js部分
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script>
const watchExampleVM = Vue.createApp({
data() {
return {
question: '',
answer: 'Questions usually contain a question mark. ;-)'
}
},
watch: {
// whenever question changes, this function will run
question(newQuestion, oldQuestion) {
if (newQuestion.indexOf('?') > -1) {
this.getAnswer()
}
}
},
methods: {
getAnswer() {
this.answer = 'Thinking...'
axios
.get('https://yesno.wtf/api')
.then(response => {
this.answer = response.data.answer
})
.catch(error => {
this.answer = 'Error! Could not reach the API. ' + error
})
}
}
}).mount('#watch-example')
</script>
在这个示例中,使用watch
选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
计算属性 vs 侦听器 适用情况
- 计算属性缓存需要依赖的数据,数据没有发生变化的时候,不会重复执行
get
方法中的业务;当数据发生变化的时候,执行计算属性的get
。计算属性可以根据需要重写set
方法。 - 侦听器监听数据变化,适用于需要在数据变化时执行异步或开销较大的操作时。