Vue 中的 computed 与 watch:深度剖析与实践应用
Vue 中的 computed 与 watch:深度剖析与实践应用
在 Vue 3 的开发世界里,computed
和watch
作为两个关键的响应式 API,对于构建高效、灵活且易于维护的前端应用起着至关重要的作用。无论是处理数据变化,还是执行异步操作,它们都有着独特的价值。今天,就让我们深入探讨这两个 API 的区别、用法以及实际应用场景。
一、用途和行为特性大揭秘
(一)computed:计算属性的魔法
computed
主要用于创建基于其他响应式数据的计算属性。它就像是一个智能加工厂,根据输入的响应式数据,经过特定的计算逻辑,输出一个新的属性值。
其行为特性十分有趣。通过getter
函数返回计算后的值,并且会自动追踪依赖的变化。一旦依赖的数据发生变动,computed
属性就会自动更新。更棒的是,它拥有缓存机制,当依赖数据没有变化时,多次访问computed
属性,它会直接返回之前的计算结果,而不会重新执行复杂的计算逻辑,这在提升性能方面效果显著。
举个例子,在一个电商购物车的场景中,我们需要实时计算商品的总价。商品的数量和单价都是响应式数据,通过computed
属性来计算总价就再合适不过了。这样,当商品数量或单价发生变化时,总价会自动更新,而且在多次访问总价时,如果数量和单价没有改变,就直接使用缓存结果,大大提高了计算效率。同时,它非常适用于简化模板中的复杂表达式。假设模板中需要展示一个经过多步计算得出的复杂数据,如果直接在模板中编写计算逻辑,代码会变得冗长且难以维护。而使用computed
属性,将计算逻辑封装起来,在模板中直接使用计算后的属性,能让代码更加清晰易读。
(二)watch:数据变化的守护者
watch
则主要用于侦听某个数据的变化,并在数据变化时执行特定的副作用,比如异步操作、条件判断等。它就像一个忠诚的卫士,时刻监听着数据的一举一动,一旦数据有变化,就立即执行相应的操作。
当数据变化时,watch
会触发回调函数。不过要注意,如果数据频繁变化,回调函数可能会多次触发,这就可能会带来性能问题。所以在使用watch
时,尤其是回调函数中包含复杂逻辑时,需要特别关注性能优化。
例如,在一个搜索功能中,用户每输入一个字符,都需要向服务器发起搜索请求。这时就可以使用watch
来侦听搜索框输入值的变化,一旦值发生改变,就触发搜索请求这个异步操作。但如果用户输入速度很快,频繁触发搜索请求可能会对服务器造成压力,也会影响用户体验,这时候就需要考虑防抖或节流等优化策略。
二、语法全解析
(一)computed 的语法奥秘
在组合式 API 中,创建computed
属性非常简单,通过computed
函数即可实现。计算属性默认是只读的,不过也可以定义可写的计算属性,这需要借助setter
函数。
import { ref, computed } from 'vue';
const items = ref([10, 20, 30, 40]);
const total = computed(() => items.value.reduce((sum, item) => sum + item, 0));
在这个例子中,total
就是一个computed
属性,它依赖于items
。当items
中的数据发生变化时,total
会自动重新计算。
如果想要创建一个可写的计算属性,可以这样做:
<template>
<div>
<p>原始数字: {{ number }}</p>
<p>平方数字: {{ squaredNumber }}</p>
<input v-model.number="number">
</div>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const number = ref(4);
const squaredNumber = computed({
get: () => number.value * number.value,
set: (newVal) => {
number.value = Math.sqrt(newVal);
}
});
return {
number,
squaredNumber
};
}
};
</script>
这里的squaredNumber
就是一个可写的计算属性。当修改squaredNumber
的值时,setter
函数会被触发,从而更新number
的值。
(二)watch 的语法细节
同样在组合式 API 中,通过watch
函数来创建侦听器。它可以精确指定要侦听的数据源,并且在数据源变化时执行回调函数。watch
函数返回一个停止侦听的函数,方便在需要时停止侦听。
watch
还有一些非常实用的额外选项:
immediate
:设置为true
时,侦听器会在创建时立即触发回调,之后响应式数据变化时继续执行回调。比如在初始化页面时,就需要根据某个初始数据执行一些操作,这时就可以使用immediate
选项。deep
:设置为true
时,侦听器会深度侦听对象内部属性的变化。当需要监听一个对象内部深层次属性的变化时,这个选项就派上用场了。
import { ref, watch } from 'vue';
const query = ref('');
watch(query, (newQuery, oldQuery) => {
// 执行异步操作,如API请求
fetch(`https://api.example.com/search?q=${newQuery}`).then(response => response.json()).then(data => {
// 更新数据
});
}, { immediate: true, deep: false });
在这个示例中,当query
的值发生变化时,就会触发fetch
请求这个异步操作。同时,因为设置了immediate: true
,所以在侦听器创建时也会执行一次回调。
三、实战演练:在项目中灵活运用
(一)computed 的实战应用
假设有一个需求,在页面上展示一段消息的反转内容。当用户输入消息时,反转后的消息也实时更新。使用computed
可以轻松实现:
<template>
<div>
<p>原始消息: {{ message }}</p>
<p>反转消息: {{ reversedMessage }}</p>
<input v-model="message">
</div>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const message = ref('Hello, Vue 3!');
const reversedMessage = computed(() => {
return message.value.split('').reverse().join('');
});
return {
message,
reversedMessage
};
}
};
</script>
在这个例子中,reversedMessage
是一个依赖于message
的计算属性。当message
的值发生变化时,reversedMessage
会自动重新计算并更新页面展示。
(二)watch 的实战应用
- 基本使用:监听单一数据变化
还是以消息输入为例,这次我们使用watch
来监听消息的变化,并在变化时打印新旧值:
<template>
<div>
<p>原始消息: {{ message }}</p>
<input v-model="message">
</div>
</template>
<script>
import { ref, watch } from 'vue';
export default {
setup() {
const message = ref('Hello, Vue 3!');
watch(message, (newValue, oldValue) => {
console.log(`Message changed from "${oldValue}" to "${newValue}"`);
});
return {
message
};
}
};
</script>
当用户在输入框中修改消息内容时,watch
的回调函数就会被触发,打印出消息的新旧值。
2. 监听多个源
在一些场景中,需要同时监听多个数据的变化。比如在一个表单中,有两个输入框,分别控制一个矩形的宽度和高度,当宽度或高度发生变化时,都需要重新计算矩形的面积。这时就可以使用watch
监听多个源:
<template>
<div>
<p>First: {{ first }}</p>
<p>Second: {{ second }}</p>
<input v-model.number="first">
<input v-model.number="second">
</div>
</template>
<script>
import { ref, watch } from 'vue';
export default {
setup() {
const first = ref(1);
const second = ref(2);
watch([first, second], ([newFirst, newSecond], [oldFirst, oldSecond]) => {
console.log(`First changed from ${oldFirst} to ${newFirst}`);
console.log(`Second changed from ${oldSecond} to ${newSecond}`);
});
return {
first,
second
};
}
};
</script>
在这个例子中,watch
同时监听first
和second
两个数据的变化,当其中任何一个发生变化时,都会执行回调函数,打印出变化前后的值。
3. 深度侦听
当需要监听一个对象内部深层次属性的变化时,deep
选项就发挥作用了。比如有一个用户对象,包含姓名、年龄等属性,当用户修改姓名时,我们需要执行一些操作:
<template>
<div>
<p>User: {{ user.name }}</p>
<input v-model="user.name">
</div>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
setup() {
const user = reactive({ name: 'John Doe' });
watch(user, (newValue, oldValue) => {
console.log(`User changed from`, oldValue, `to`, newValue);
}, { deep: true });
return {
user
};
}
};
</script>
这里通过设置deep: true
,watch
可以深度监听user
对象内部name
属性的变化。
4. 立即执行
有时候,我们希望在侦听器创建时就立即执行一次回调函数,比如在页面加载时,根据初始数据执行一些初始化操作。这时候就可以使用immediate
选项:
<template>
<div>
<p>Message: {{ message }}</p>
<input v-model="message">
</div>
</template>
<script>
import { ref, watch } from 'vue';
export default {
setup() {
const message = ref('Hello, Vue 3!');
watch(message, (newValue, oldValue) => {
console.log(`Message changed from "${oldValue}" to "${newValue}"`);
}, { immediate: true });
return {
message
};
}
};
</script>
在这个例子中,因为设置了immediate: true
,所以在页面加载时,watch
的回调函数就会立即执行一次,打印出初始的新旧值。
四、总结:computed 与 watch 的选择之道
通过以上的详细介绍,我们对computed
和watch
的区别与用法有了更深入的理解。在实际开发中,选择使用computed
还是watch
,需要根据具体的需求来决定。
如果需要创建一个基于其他响应式依赖的计算属性,并且希望高效地缓存计算结果,同时在模板中使用这个计算属性来简化代码,那么computed
是不二之选。它不仅能提升性能,还能让代码更加清晰易读。
而当需要侦听响应式引用或计算属性的变化,并在变化时执行副作用,比如异步操作、复杂的条件判断等,watch
则能发挥它的优势。并且watch
还提供了诸如监听多个源、深度侦听、立即执行等灵活的选项,满足各种复杂场景的需求。