◆
Vue computed 与 watch 的区别及 watchEffect 用法
计算属性 Computed :
- 支持缓存,只有依赖数据(
vm中data的属性)发生改变,才会重新进行计算- 缓存的作用
如下所示,
computed与method都对data中的数据进行了再一次的处理,都能达到相同的效果。 但是不同的是计算属性是基于它们的反应依赖关系缓存的。 计算属性只在相关响应式依赖发生改变时它们才会重新求值。 这就意味着只要author.books还没有发生改变, 多次访问calculateBooksMessage计算属性会立即返回之前的计算结果,而不必再次执行函数。 我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性list,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于list。如果没有缓存, 我们将不可避免的多次执行list的getter!如果你不希望有缓存,请用method来替代。
- 缓存的作用
computed: {
// 计算属性的 getter
calculateBooksMessage();
{
// `this` 指向 vm 实例
return this.author.books.length > 0 ? 'Yes' : 'No';
}
}
methods: {
calculateBooksMessage();
{
return this.author.books.length > 0 ? 'Yes' : 'No';
}
}
- 不支持异步,当
computed内有异步操作时无效,无法监听数据的变化 computed属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过的数据通过计算得到的- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用
computed - 如果
computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。 - 当模板中的数据需要其他逻辑处理时可以用
computed处理(对于任何包含响应式数据的复杂逻辑,都应该使用计算属性) - 使用场景:当一个值受多个属性影响的时候,
监听属性 watch:
- 不支持缓存,数据变化,直接会触发相应的操作;
watch支持异步;- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
watch只会监听数据的值是否发生改变,而不会去监听数据的地址是否发生改变。也就是说,watch想要监听引用类型数据的变化,需要进行深度监听。"obj.name"(){}------如果obj的属性太多,这种方法的效率很低,obj:{handler(newVal){},deep:true}------用handler+deep的方式进行深度监听。- 使用场景:当一条数据的更改影响到多条数据的时候
区别:
功能上:computed 是计算属性,watch 是监听一个值的变化,然后执行对应的回调。
- 是否调用缓存:
computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。 - 是否调用
return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。 - 使用场景:
computed----当一个属性受多个属性影响的时候,使用computed-------购物车商品结算。watch----当一条数据影响多条数据的时候,使用watch-------搜索框。
vue2 中的 computed
<template>
苹果数量:
<input v-model="count.numberOfApples" />
<br />
西瓜数量:
<input v-model="count.numberOfWatermelons" />
<br />
鸡蛋数量:
<input v-model="count.numberOfEggs" />
<br />
总数量:
<input v-model="sum" />
<br />
苹果数量 + 西瓜数量:
<input v-model="sumSecond" />
<br />
</template>
<script lang="ts">
export default {
data() {
return {
count: {
numberOfApples: 0,
numberOfWatermelons: 0,
numberOfEggs: 0,
},
};
},
mounted() {
// this.sumSecond = 10;
// console.log(this.sumSecond);
},
computed: {
// 计算属性的 getter
sum() {
return (
Number(this.count.numberOfApples) +
Number(this.count.numberOfWatermelons) +
Number(this.count.numberOfEggs)
);
},
sumSecond: {
get() {
// 当模板中 sumSecond 没有被依赖时,即使 data 里的值改变了,也不会触发 get 函数
console.log('sumSecond 的 get 触发');
return (
Number(this.count.numberOfApples) +
Number(this.count.numberOfWatermelons)
);
},
set(val) {
// 当 sumSecond 变化时主动出发 set 函数
console.log('sumSecond 的 set 触发', val);
},
},
},
};
</script>
vue3 中的 computed
<template>
苹果数量:
<input v-model="count.numberOfApples" />
<br />
西瓜数量:
<input v-model="count.numberOfWatermelons" />
<br />
鸡蛋数量:
<input v-model="count.numberOfEggs" />
<br />
总数量:
<input v-model="sum" />
<br />
苹果数量 + 西瓜数量:
<input v-model="sumSecond" />
<br />
</template>
<script lang="ts">
import { computed, defineComponent, reactive } from 'vue';
export default defineComponent({
setup(prop) {
const count: any = reactive({
numberOfApples: 0,
numberOfWatermelons: 0,
numberOfEggs: 0,
});
const sum = computed(() => {
return (
Number(count.numberOfApples) +
Number(count.numberOfWatermelons) +
Number(count.numberOfEggs)
);
});
const sumSecond = computed(() => {
return Number(count.numberOfApples) + Number(count.numberOfWatermelons);
});
return {
sumSecond,
sum,
count,
};
},
});
</script>
vue2 中的 watch
<template>
<div>
<input type="text" v-model="watchTextOne" />
<br />
<br />
<input type="text" v-model="watchTextTwo" />
<br />
<br />
<input type="text" v-model="watchTextThree" />
<br />
<br />
<input type="text" v-model="watchTextFour.name" />
<br />
<br />
<input type="text" v-model="watchTextFour.age" />
<br />
<br />
<input type="text" v-model="watchTextFour.feature" />
<br />
<br />
<input type="text" v-model="watchTextFour.feature[0]" />
</div>
</template>
<script>
export default {
data() {
return {
watchTextOne: '',
watchTextTwo: '',
watchTextThree: '',
watchTextFour: {
name: 'fx',
age: 18,
feature: ['smart', 'great'],
},
};
},
watch: {
// 第一种watch监听方式
watchTextOne(newVal, oldVal) {
console.log('watchTextOne新值:', newVal);
console.log('watchTextOne旧值:', oldVal);
this.watchTextTwo = newVal;
this.watchTextThree = oldVal;
},
/*
注意到handler了吗,我们给 firstName 绑定了一个handler方法,之前我们写的 watch 方法其实默认写的就是这个handler,Vue.js会去处理这个逻辑,最终编译出来其实就是这个handler。
而immediate:true代表如果在 wacth 里声明了 firstName 之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行。
*/
watchTextThree: {
handler(newVal, oldVal) {
console.log('watchTextThree新值:', newVal);
console.log('watchTextThree旧值:', oldVal);
},
immediate: true, // immediate表示在watch中首次绑定的时候,是否执行handler
},
watchTextFour: {
handler(newVal, oldVal) {
console.log('watchTextFour新值:', newVal);
console.log('watchTextFour旧值:', oldVal);
},
deep: true, // 此时会给watchTextFour的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。
},
'watchTextFour.age': {
handler(newVal, oldVal) {
console.log('watchTextFour.age新值:', newVal);
console.log('watchTextFour.age旧值:', oldVal);
},
deep: true,
},
},
};
</script>
vue3 中的 watch
- watchEffect 函数
- 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
- 默认初始时就会执行第一次, 从而可以收集需要监视的数据
- 监视数据发生变化时回调
- watchEffect 拿不到新值与旧值(更新前的值)
<template>
<div>
watchTextOne:
<input type="text" v-model="watchTextOne" />
<br />
<br />
watchTextTwo:
<input type="text" v-model="watchTextTwo" />
<br />
<br />
watchTextThree:
<input type="text" v-model="watchTextThree" />
<br />
<br />
watchTextSum:
<input type="text" v-model="watchTextSum" />
<br />
<br />
watchTextFour.name:
<input type="text" v-model="watchTextFour.name" />
<br />
<br />
watchTextFour.age:
<input type="text" v-model="watchTextFour.age" />
<br />
<br />
watchTextFour.feature:
<input type="text" v-model="watchTextFour.feature" />
<br />
<br />
watchTextFour.feature[0]:
<input type="text" v-model="watchTextFour.feature[0]" />
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, watch, watchEffect } from 'vue';
export default defineComponent({
setup(prop) {
const watchTextOne = ref(2),
watchTextTwo = ref(1),
watchTextThree = ref(3),
watchTextSum = ref(0),
watchTextFour = reactive({
name: 'fx',
age: 18,
feature: ['smart', 'great'],
});
watch(
watchTextTwo,
() => {
watchTextOne.value = watchTextTwo.value + watchTextThree.value;
},
{
immediate: true, // 是否初始化立即执行一次, 默认是false
deep: true, // 是否是深度监视, 默认是false
},
);
watch([() => watchTextFour.age, () => watchTextFour.name], values => {
console.log('监视多个数据', values);
});
// 可实现类似 computer 一个值受多个值影响的效果
watchEffect(() => {
watchTextSum.value =
Number(watchTextOne.value) +
Number(watchTextTwo.value) +
Number(watchTextThree.value);
});
return {
watchTextOne,
watchTextTwo,
watchTextThree,
watchTextFour,
watchTextSum,
};
},
});
</script>
计算属性 vs 侦听器
Vue 提供了一种更通用的方式来观察和响应当前活动的实例上的数据变动:**侦听属性 **。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:
<div id="demo">{{ fullName }}</div>
const vm = Vue.createApp({
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar',
};
},
watch: {
firstName(val) {
this.fullName = val + ' ' + this.lastName;
},
lastName(val) {
this.fullName = this.firstName + ' ' + val;
},
},
}).mount('#demo');
上面代码是命令式且重复的。将它与计算属性的版本进行比较:
const vm = Vue.createApp({
data() {
return {
firstName: 'Foo',
lastName: 'Bar',
};
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName;
},
},
}).mount('#demo');
watchEffect
如果有多个负效应,不要粘合在一起,建议写多个 watchEffect。
watchEffect(() => {
setTimeout(() => console.log(a.val + 1), 1000);
setTimeout(() => console.log(b.val + 1), 1000);
});
这两个 setTimeout 是两个不相关的效应,不需要同时监听 a 和 b,分开写吧:
watchEffect(() => {
setTimeout(() => console.log(a.val + 1), 1000);
});
watchEffect(() => {
setTimeout(() => console.log(b.val + 1), 1000);
});

还没有留言,来发表第一条吧~