好的,我们开始。
Vue 3 的 computed
:深入理解 Getter 与 Setter
大家好,今天我们来深入探讨 Vue 3 中 computed
属性的 getter
和 setter
。computed
属性是 Vue 中一个非常重要的概念,它允许我们声明依赖于其他响应式数据的属性,并自动缓存结果。理解 getter
和 setter
的工作原理对于编写高效、可维护的 Vue 应用至关重要。
computed
的基本概念
在 Vue 中,computed
属性是一个函数,它的返回值会被缓存,只有当依赖的响应式数据发生改变时,才会重新计算。这使得 computed
属性非常适合用于处理复杂的计算逻辑,而无需担心性能问题。
基本语法如下:
<template>
<p>Message: {{ message }}</p>
<p>Reversed Message: {{ reversedMessage }}</p>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const message = ref('Hello Vue!');
const reversedMessage = computed(() => {
return message.value.split('').reverse().join('');
});
return {
message,
reversedMessage
};
}
};
</script>
在这个例子中,reversedMessage
是一个 computed
属性,它依赖于 message
。当 message
的值发生改变时,reversedMessage
会自动重新计算。
getter
:获取计算值
每个 computed
属性都必须有一个 getter
函数。getter
函数负责计算并返回 computed
属性的值。在上面的例子中,我们直接提供了一个函数作为 computed
属性,实际上,这个函数就是 getter
。
const reversedMessage = computed(() => {
return message.value.split('').reverse().join(''); // This is the getter
});
getter
函数可以访问组件的任何响应式数据,并使用这些数据进行计算。getter
函数的返回值就是 computed
属性的值。
setter
:修改计算值
默认情况下,computed
属性是只读的。这意味着我们不能直接修改 computed
属性的值。但是,我们可以通过提供 setter
函数来使 computed
属性可写。
setter
函数允许我们拦截对 computed
属性的赋值操作,并在赋值操作发生时执行自定义的逻辑。这使得我们可以根据 computed
属性的新值来更新其他响应式数据。
如何定义 setter
要定义 setter
,我们需要将 computed
属性定义为一个对象,而不是一个函数。该对象包含 get
和 set
两个属性,分别对应 getter
和 setter
函数。
<template>
<p>First Name: {{ firstName }}</p>
<p>Last Name: {{ lastName }}</p>
<p>Full Name: {{ fullName }}</p>
<input type="text" v-model="fullName" />
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const firstName = ref('John');
const lastName = ref('Doe');
const fullName = computed({
get: () => {
return firstName.value + ' ' + lastName.value;
},
set: (newValue) => {
const names = newValue.split(' ');
firstName.value = names[0] || '';
lastName.value = names[1] || '';
}
});
return {
firstName,
lastName,
fullName
};
}
};
</script>
在这个例子中,fullName
是一个可写的 computed
属性。它的 getter
函数返回 firstName
和 lastName
的组合。它的 setter
函数将新的 fullName
值拆分为 firstName
和 lastName
,并更新相应的响应式数据。
setter
的参数
setter
函数接收一个参数,该参数是 computed
属性的新值。我们可以使用这个新值来更新其他响应式数据。
何时使用 setter
当我们需要根据 computed
属性的值来更新其他响应式数据时,就需要使用 setter
。以下是一些常见的用例:
- 双向绑定: 如上面的例子,
fullName
属性双向绑定到输入框,修改输入框的值会同时修改firstName
和lastName
。 - 数据转换: 当我们需要在
computed
属性的值和底层数据之间进行转换时,可以使用setter
。 - 副作用: 当我们需要在
computed
属性的值发生改变时执行某些副作用时,可以使用setter
。例如,我们可以使用setter
来更新本地存储或发送网络请求。
getter
和 setter
的注意事项
- 避免副作用:
getter
函数应该只负责计算和返回值,不应该执行任何副作用。副作用应该放在setter
函数中执行。 - 避免循环依赖: 避免创建循环依赖的
computed
属性。例如,A
依赖于B
,而B
又依赖于A
,这会导致无限循环。 - 性能优化: 尽量避免在
getter
函数中执行复杂的计算逻辑。如果计算逻辑过于复杂,可以考虑使用watch
监听响应式数据的变化,并在watch
回调函数中执行计算逻辑。 - 正确处理
undefined
和null
: 在getter
和setter
函数中,要正确处理undefined
和null
值,避免出现意外的错误。
computed
与 watch
的比较
computed
和 watch
都是 Vue 中用于响应式数据的重要机制。它们之间的主要区别在于:
特性 | computed |
watch |
---|---|---|
目的 | 计算并缓存派生值 | 监听响应式数据的变化并执行副作用 |
触发时机 | 依赖的响应式数据发生改变时 | 监听的响应式数据发生改变时 |
返回值 | 返回计算后的值 | 没有返回值(通常用于执行副作用) |
缓存 | 有缓存机制 | 没有缓存机制 |
适用场景 | 需要计算并缓存派生值时 | 需要监听响应式数据的变化并执行副作用时 |
异步操作 | 不适合直接进行异步操作(除非使用 async computed ) |
适合进行异步操作 |
一般来说,如果我们需要计算并缓存派生值,应该使用 computed
。如果我们需要监听响应式数据的变化并执行副作用,应该使用 watch
。
实例分析:使用 computed
实现购物车总价计算
假设我们有一个购物车组件,其中包含多个商品。每个商品都有一个价格和一个数量。我们需要计算购物车中所有商品的总价。
<template>
<ul>
<li v-for="item in cartItems" :key="item.id">
{{ item.name }} - ${{ item.price }} x {{ item.quantity }}
</li>
</ul>
<p>Total: ${{ totalPrice }}</p>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const cartItems = ref([
{ id: 1, name: 'Product A', price: 10, quantity: 2 },
{ id: 2, name: 'Product B', price: 20, quantity: 1 },
{ id: 3, name: 'Product C', price: 5, quantity: 3 }
]);
const totalPrice = computed(() => {
return cartItems.value.reduce((total, item) => {
return total + item.price * item.quantity;
}, 0);
});
return {
cartItems,
totalPrice
};
}
};
</script>
在这个例子中,totalPrice
是一个 computed
属性,它依赖于 cartItems
。当 cartItems
中的任何一个商品的数量发生改变时,totalPrice
会自动重新计算。
扩展:Async Computed (实验性)
VueUse 库提供了一个 useAsyncComputed
函数,允许我们在 computed
属性中使用异步操作。这对于从 API 获取数据并将其显示在模板中非常有用。
import { ref, computed } from 'vue';
import { useAsyncComputed } from '@vueuse/core';
export default {
setup() {
const userId = ref(1);
const user = useAsyncComputed(async () => {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId.value}`);
return await response.json();
}, null); // Initial value, can be null or any default value
return {
userId,
user
};
}
};
在这个例子中,user
是一个异步 computed
属性,它从 API 获取用户信息。useAsyncComputed
函数接受两个参数:一个异步函数和一个初始值。异步函数负责从 API 获取数据,初始值用于在数据加载完成之前显示。
computed
属性的调试
当 computed
属性的行为不符合预期时,我们可以使用 Vue Devtools 来调试它。Vue Devtools 允许我们查看 computed
属性的依赖关系,以及它的值是如何变化的。
我们还可以在 getter
和 setter
函数中添加 console.log
语句,以便在控制台中查看计算过程。
总结与建议
理解 Vue 3 中 computed
属性的 getter
和 setter
对于编写高效、可维护的 Vue 应用至关重要。getter
函数负责计算并返回 computed
属性的值,而 setter
函数允许我们拦截对 computed
属性的赋值操作,并在赋值操作发生时执行自定义的逻辑。记住避免副作用,避免循环依赖,并正确处理 undefined
和 null
值。
- 区分
getter
和setter
的作用,getter
用于获取值,setter
用于修改值。 - 灵活运用
computed
和watch
,根据不同的场景选择合适的工具。 - 养成良好的编码习惯,避免副作用和循环依赖。
希望今天的讲解对大家有所帮助。谢谢大家!