栗子
先来看个例子
<template>
<div>
<input type="text" placeholder="请输入待办事项" @keyup.enter="insertTodo" :value="todo">
<ul v-for="(todo, i) in todos" :key="i">
<li>{{ todo }}</li>
</ul>
<!-- 写的很复杂,读起来也复杂 模板可读性很差,不够优雅 -->
<div>待办事项:{{ todos.length + '项' }}</div>
</div>
</template>
待办事项那多少有点不优雅,要是模板中的表达式再复杂些看起来会更加杂乱,在模板中放入太多的逻辑会让模板过重且难以维护。所以对于任何复杂的逻辑。都应当使用计算属性。虽然侦听属性也可以做到让模板简洁,但是不推荐使用。原因下面会介绍到。
计算属性:
<template>
<div>
<input type="text" placeholder="请输入待办事项" @keyup.enter="insertTodo" :value="todo">
<ul v-for="(todo, i) in todos" :key="i">
<li>{{ todo }}</li>
</ul>
<div>待办事项:{{ total }}</div>
</div>
</template>
<script>
export default {
name: 'Computed',
data () {
return {
todo: '',
todos: ['买菜']
}
},
methods: {
insertTodo (e) {
if (e.target.value) {
this.todo = ''
this.todos.push(e.target.value)
this.total = '哈哈'
}
}
},
computed: {
total () {
return this.todos.length + '项'
}
}
}
</script>
计算属性computed是一个对象,对象中的key值是一个声明后会被塞进data响应数据中的值,return出来的就是该key的值。computed中的key与data中的key具有相同的使用方式。
也许你会问,为什么要在computed中去返回,直接在method中去设置复制不是一样的吗?computed与method相比,除了可以完成业务功能外,还具有缓存性,计算属性是基于它们的响应式依赖进行缓存的。也就是说上述例子中total所依赖的todos只要不改变,计算属性会立即返回之前的计算结果,而不必再次执行函数重新求值。相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
为甚要有缓存
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
计算属性的 setter
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
computed: {
total: {
get () {
return this.todos.length + '项'
},
set (val) {
// 可以在这里做一些操作
console.log('set乐乐乐')
}
}
}
上述例子中,如果对total进行赋值,就会进入set函数。比如
this.total = 'XXX'
// 就会打印
"set乐乐乐"
侦听器:
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。
比如上述需求通过watch一样可以完成:
watch: {
todos (newVal, oldVal) {
this.count = newVal.length + '项'
}
},
watch接受一个对象,key是需要监测侦听的值,值是一个拥有该被检测的值的改变前的值和改变后的值两个参数的函数。函数中可以做一些该值发生改变后需要进行的一些逻辑操作。
上述代码,在增加todos项时,可以看到count会发生相应改变,但是第一次进来的时候,count是没有值的,这是因为watch只有在检测的数据发生改变的时候才会触发~
所以在一般的时候,并不建议使用watch,而是建议使用computed。只有当需要在数据变化时执行异步或开销较大的操作时,才使用watch。
如何让watch一进来就触发???
设置immediate为true,该配置的意思是,立即触发。
还有另外一个比较有用的option,deep。当我们的todos是个多层数据时,比如对象数组,或者更深的层级,我们之间进行监听是监听不到他的改变的,设置true可以深层监听。
watch: {
todos: {
immediate: true,
deep: true,
handler (newVal) {
this.count = newVal.length + '项'
}
}
},
computed VS watch
- 两者处理数据的场景不同,监听器(watch)适合一个数据影响多个数据,计算属性(computed)适合一个数据受多个数据影响
- 计算属性有缓存性,计算所得的值如果没有变化不会重复执行
- 监听器选项提供了更通用的方法,适合执行异步操作或较大开销操作的情况