一、Computed
可以关联多个实时计算的对象,当这些对象中的其中一个改变时都会触发这个属性,具有缓存能力,所以只有当数据再次改变时才会重新渲染,否则就会直接拿取缓存中的数据。
在 Vue.js 中,computed
属性(计算属性)提供了一种高效的方式来计算和缓存基于响应式依赖的值。计算属性的主要特点是它们的值是基于依赖项的变化而自动更新的,并且只有在依赖项发生变化时才重新计算。这种机制使得计算属性具有很高的性能,因为它们避免了不必要的重复计算。
1.1 Vue2 中的 computed
在 Vue 2 中,计算属性在其依赖的数据变化时会立即重新计算和更新。这意味着每次依赖的数据发生变化时,计算属性的 getter
会同步执行,以确保计算属性的值始终是最新的。
工作机制:
- 依赖收集:当计算属性的
getter
第一次被访问时,Vue 会收集依赖项。 - 依赖更新:当依赖项发生变化时,Vue 会立即重新计算计算属性的值。
- 缓存机制:如果计算属性在依赖项没有变化的情况下被多次访问,它会返回缓存的值,而不会重新计算。
const vm = new Vue({
data() {
return {
a: 1,
b: 2
};
},
computed: {
sum() {
console.log('Calculating sum...');
return this.a + this.b;
}
}
});
vm.a = 2; // 立即触发重新计算
console.log(vm.sum); // 输出 "Calculating sum..." 和 4
1.2 Vue3 中的 computed
在 Vue 3 中,计算属性的机制更加高效。计算属性在其依赖的数据变化时不会立即重新计算。相反,它会被标记为“脏”(dirty),下次访问计算属性时才会重新计算。这意味着计算属性是懒执行的,只有在真正需要时才会进行计算。
工作机制:
- 依赖收集:当计算属性的
getter
第一次被访问时,Vue 会收集依赖项。 - 标记脏值:当依赖项发生变化时,计算属性会被标记为“脏”,但不会立即重新计算。
- 懒执行:只有在下次访问计算属性时,才会重新计算其值,并更新缓存。
import { ref, computed } from 'vue';
const a = ref(1);
const b = ref(2);
const sum = computed(() => {
console.log('Calculating sum...');
return a.value + b.value;
});
a.value = 2; // 仅标记 sum 为脏值
console.log(sum.value); // 输出 "Calculating sum..." 和 4
即时更新 vs 懒执行主要区别如下:
- Vue 2:依赖项变化时,计算属性立即重新计算。
- Vue 3:依赖项变化时,计算属性被标记为脏值,下次访问时才会重新计算。
性能优化:
Vue 3 的懒执行机制避免了不必要的计算,特别是在计算开销较大的场景中,这可以显著提高性能。
例如,当我们想让div
元素的背景色和文字颜色一致时,我们就可以使用computed
属性。此时computed
只会在初次渲染和文字颜色改变的情况下才会触发。其他情况下直接从缓存中读取。
<template>
<div>
<div style="width:200px" :style="styleData">颜色改变</div>
<button @click="change"></button>
</div>
</template>
<script>
export default {
name: "button",
data() {
return {
color: 'red'
}
},
computed: {
styleData: funtion () {
return {
backgroundColor: this.color
}
}
}
}
</script>
1.3 应用场景
在 Vue.js 中,computed
属性(计算属性)因其高效的缓存机制和自动更新功能,适用于多种场景。以下是一些常见的应用场景及详细解释:
- 数据转换与格式化
计算属性可以用于将原始数据进行转换或格式化,以便在模板中展示。例如,将日期对象格式化为可读字符串,或者对数值进行货币格式化。
data() {
return {
price: 1234.56
};
},
computed: {
formattedPrice() {
return `${this.price.toFixed(2)}`;
}
}
- 基于依赖数据的派生状态
计算属性适合用于创建派生状态,这些状态基于其他数据的变化。例如,计算表单的有效性或过滤列表数据。
data() {
return {
items: ['apple', 'banana', 'orange', 'grape'],
filterText: 'a'
};
},
computed: {
filteredItems() {
return this.items.filter(item => item.includes(this.filterText));
}
}
- 复杂计算逻辑
对于复杂的计算逻辑,使用计算属性可以将计算与模板分离,使模板代码更加清晰易读。例如,计算购物车中商品的总价格。
data() {
return {
cart: [
{ name: 'apple', price: 1.0, quantity: 3 },
{ name: 'banana', price: 0.5, quantity: 5 }
]
};
},
computed: {
totalPrice() {
return this.cart.reduce((total, item) => total + item.price * item.quantity, 0);
}
}
- 多个依赖数据的组合
当需要组合多个数据源的值时,计算属性非常有用。例如,计算用户的全名或基于多个输入字段的结果。
data() {
return {
firstName: 'John',
lastName: 'Doe'
};
},
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
- 动态样式和类绑定
计算属性可以用于计算动态样式或类绑定的值,根据组件的状态变化自动更新。例如,根据评分设置星级样式。
data() {
return {
rating: 4
};
},
computed: {
starClass() {
return `star-${this.rating}`;
}
}
- 表单输入的双向绑定
使用计算属性可以处理复杂的表单输入逻辑,如将多个输入字段的值组合成一个对象或字符串。
data() {
return {
userInput: ''
};
},
computed: {
formattedInput: {
get() {
return this.userInput.toUpperCase();
},
set(value) {
this.userInput = value.toLowerCase();
}
}
}
- 优化性能
在需要频繁访问的场景中,计算属性可以避免不必要的重新计算。例如,实时显示搜索结果的数量,基于过滤条件的变化自动更新。
data() {
return {
items: ['apple', 'banana', 'orange', 'grape'],
searchQuery: 'a'
};
},
computed: {
searchResults() {
return this.items.filter(item => item.includes(this.searchQuery));
},
resultsCount() {
return this.searchResults.length;
}
}
二、Watch
当你需要在数据变化响应时,执行异步操作,或高性能消耗的操作,自定义watch
方式就会很有帮助。