1.1 什么是计算属性?
计算属性是Vue中一个非常实用的特性,它允许我们基于已有的数据属性计算出新的属性值。简单来说,计算属性就是通过data中已定义的属性计算得来的属性。
原理:Vue底层使用defineProperty
方法提供的getter和setter来实现计算属性。
1.2 计算属性的特点
缓存机制:计算属性会缓存计算结果,只有依赖的属性发生变化时才会重新计算
响应式:当依赖的数据变化时,计算属性会自动更新
可读写:默认提供getter,也可以设置setter
1.3 基本用法示例
<div id="app">
<p>原始价格: {{ price }}元</p>
<p>折扣后价格: {{ discountedPrice }}元</p>
</div>
<script>
new Vue({
el: '#app',
data: {
price: 100,
discount: 0.8
},
computed: {
// 计算属性
discountedPrice: function() {
return this.price * this.discount;
}
}
});
</script>
在这个例子中,discountedPrice
是一个计算属性,它依赖于price
和discount
两个数据属性。当其中任何一个变化时,discountedPrice
都会自动重新计算。
1.4 getter和setter
计算属性默认只有getter,但也可以提供setter:
computed: {
fullName: {
// getter
get: function() {
return this.firstName + ' ' + this.lastName;
},
// setter
set: function(newValue) {
var names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[names.length - 1];
}
}
}
注意:如果希望修改计算属性,必须在计算属性中增加set函数,并且set函数的实现必须改变依赖的属性,这样才能触发get函数的调用。
1.5 计算属性的简写形式
当计算属性只需要getter时,可以使用简写形式:
computed: {
discountedPrice() { // 等同于 discountedPrice: function()
return this.price * this.discount;
}
}
二、监视属性(watch)
2.1 什么是监视属性?
监视属性允许我们观察和响应Vue实例上的数据变动。当被监视的属性发生变化时,指定的回调函数会自动调用。
2.2 基本用法
<div id="app">
<p>计数器: {{ counter }}</p>
<button @click="counter++">增加</button>
</div>
<script>
new Vue({
el: '#app',
data: {
counter: 0
},
watch: {
counter: function(newVal, oldVal) {
console.log('计数器从', oldVal, '变为', newVal);
if (newVal >= 10) {
alert('计数器已达到10!');
}
}
}
});
</script>
2.3 监视属性的配置选项
监视属性可以接受一个对象,包含以下选项:
handler
:变化时调用的函数immediate
:是否在初始化时立即调用一次handlerdeep
:是否深度监视对象内部值的变化
watch: {
user: {
handler: function(newVal, oldVal) {
console.log('用户信息变化了');
},
immediate: true, // 初始化时调用一次
deep: true // 深度监视
}
}
2.4 深度监视
Vue中的watch默认不监测对象内部值的变化。配置deep:true
可以监测对象内部值的变化。
data: {
user: {
name: '张三',
age: 25
}
},
watch: {
user: {
handler(newVal) {
console.log('用户信息变化:', newVal);
},
deep: true
}
}
也可以单独监视对象内部的某个属性:
watch: {
'user.name': function(newVal) {
console.log('用户名变为:', newVal);
}
}
2.5 监视的简写形式
当只需要handler函数时,可以使用简写:
// 第一种简写
watch: {
counter(newVal, oldVal) {
console.log('计数器变化:', oldVal, '->', newVal);
}
}
// 第二种简写
vm.$watch('counter', function(newVal, oldVal) {
console.log('计数器变化:', oldVal, '->', newVal);
});
三、计算属性 vs 监视属性
3.1 主要区别
特性 | 计算属性(computed) | 监视属性(watch) |
---|---|---|
用途 | 基于依赖计算新值 | 观察数据变化执行回调 |
缓存 | 有缓存,依赖不变不重新计算 | 无缓存,每次变化都执行回调 |
异步 | 不支持异步操作 | 支持异步操作 |
返回值 | 必须返回一个值 | 不需要返回值 |
适用场景 | 模板中需要显示的计算结果 | 数据变化时需要执行的操作 |
3.2 何时使用哪个?
使用计算属性:
需要基于现有数据计算新值
需要在模板中使用的派生数据
需要缓存优化性能的场景
使用监视属性:
需要在数据变化时执行异步操作
需要执行复杂逻辑或副作用
需要观察特定数据的变化并做出反应
3.3 综合案例
<div id="app">
<input v-model="firstName" placeholder="名">
<input v-model="lastName" placeholder="姓">
<p>全名(计算属性): {{ fullName }}</p>
<p>全名(监视属性): {{ fullName2 }}</p>
<p>问题: {{ question }}</p>
<p>答案: {{ answer }}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三',
fullName2: '张 三',
question: '',
answer: '请提出问题...'
},
computed: {
// 计算属性实现全名
fullName() {
return this.firstName + ' ' + this.lastName;
}
},
watch: {
// 监视属性实现全名
firstName(newVal) {
this.fullName2 = newVal + ' ' + this.lastName;
},
lastName(newVal) {
this.fullName2 = this.firstName + ' ' + newVal;
},
// 异步示例:回答问题
question(newVal) {
this.answer = '思考中...';
// 模拟异步请求
setTimeout(() => {
this.answer = newVal.includes('?') ? '这是一个好问题!' : '这似乎不是一个问题';
}, 1000);
}
}
});
</script>
四、重要原则
this指向问题:
所有被Vue管理的函数(如methods、computed、watch中的函数),建议写成普通函数,这样this指向的是Vue实例(vm)
所有不被Vue管理的函数(如定时器回调、AJAX回调等),最好写成箭头函数,这样this才能继续指向Vue实例
watch: {
data() {
// 这里的this指向Vue实例
setTimeout(() => {
// 使用箭头函数,this仍然指向Vue实例
console.log(this.data);
}, 1000);
}
}
性能考虑:
计算属性有缓存,适合计算开销较大的操作
深度监视(
deep:true
)会带来性能开销,应谨慎使用
五、总结
计算属性和监视属性是Vue中响应式编程的两个重要工具。计算属性适合声明式的派生数据,而监视属性适合命令式的响应数据变化。理解它们的区别和适用场景,能够帮助我们写出更高效、更易维护的Vue代码。
记住:能用计算属性实现的,优先使用计算属性;当需要执行异步操作或复杂逻辑时,再考虑使用监视属性