在Vue中,我们通常使用computed属性来计算一个值,这样我们可以方便地在模板中使用这个值。但是有时候,我们会遇到这样的问题:computed里面的数据打印出来变化了,但是视图里面没有变化。本文将会深入探讨这个问题。
一、什么是computed属性
Vue中computed属性是计算属性,它在Vue实例中用于计算一个值。computed的值是只读的,它们依赖于其他属性的值,并且只有在依赖的值发生改变时才会重新求值。
例如,我们可以使用computed属性计算两个数字的和:
<template>
<div>{{ result }}</div>
</template>
<script>
export default {
data() {
return {
num1: 1,
num2: 2,
};
},
computed: {
result() {
return this.num1 + this.num2;
},
},
};
</script>
在上面的例子中,result是一个只读的computed属性,它依赖于num1和num2两个属性,只有当这两个属性发生变化时,result才会重新计算。
二、computed属性的计算过程
计算computed属性的过程是一个异步的过程,Vue会在下一次事件循环中计算computed属性。这是为了避免不必要的计算,提升性能。
下面的例子演示了computed属性的计算过程:
<template>
<div>{{ result }}</div>
<button @click="changeNum">Change Num</button>
</template>
<script>
export default {
data() {
return {
num1: 1,
num2: 2,
};
},
computed: {
result() {
console.log("computed");
return this.num1 + this.num2;
},
},
methods: {
changeNum() {
this.num1++;
},
changeNumSync() {
this.num2++;
},
},
};
</script>
在上面的例子中,当我们点击Change Num按钮时,num1属性会加1,这会导致result属性发生变化,因此Vue会在下一次事件循环中计算result属性。我们可以在浏览器的控制台中看到"computed"这个输出。
三、computed属性不更新的原因
因为computed属性的计算过程是异步的,所以如果computed属性依赖的属性发生变化时,如果Vue在下一次事件循环之前检测到了这个变化并更新了computed属性,那么视图就会自动更新。
但是,如果computed属性依赖的属性发生变化后,Vue没有在下一次事件循环之前检测到这个变化,computed属性就不会更新,视图也就不会更新。
下面的例子演示了computed属性不更新的情况:
<template>
<div>{{ result }}</div>
<button @click="changeNum">Change Num</button>
</template>
<script>
export default {
data() {
return {
num1: 1,
num2: 2,
};
},
computed: {
result() {
console.log("computed");
return this.num1 + this.num2;
},
},
methods: {
changeNum() {
this.num1++;
this.$nextTick(() => {
console.log(this.result);
});
},
},
};
</script>
在上面的例子中,我们在changeNum方法中加了一个$nextTick方法,这个方法会在下一次事件循环中执行。在$nextTick方法中,我们打印了result属性的值。这个值会比实际的值晚一步。这是因为在num1的值发生变化后,Vue没有在下一次事件循环之前检测到这个变化,computed属性没有更新,result的值也就没有更新。
四、解决computed属性不更新的方法
我们可以使用以下方法解决computed属性不更新的问题:
4.1 使用watch监听属性变化
我们可以使用watch监听num1和num2属性的变化,并在变化时强制重新计算computed属性。这样,即使Vue没有在下一次事件循环之前检测到属性的变化,computed属性也会更新。
<template>
<div>{{ result }}</div>
<button @click="changeNum">Change Num</button>
</template>
<script>
export default {
data() {
return {
num1: 1,
num2: 2,
};
},
computed: {
result() {
console.log("computed");
return this.num1 + this.num2;
},
},
watch: {
num1: {
handler() {
this.$forceUpdate();
},
},
num2: {
handler() {
this.$forceUpdate();
},
},
},
methods: {
changeNum() {
this.num1++;
},
},
};
</script>
在上面的例子中,我们使用watch监听了num1和num2属性的变化,并在变化时强制重新计算computed属性。这样,即使Vue没有在下一次事件循环之前检测到属性的变化,computed属性也会更新。
4.2 使用key让Vue重新渲染组件
当Vue检测到组件的key发生变化时,它会销毁当前的组件并重新创建一个新的组件,这样就能够强制computed属性重新计算。
<template>
<div :key="num1">{{ result }}</div>
<button @click="changeNum">Change Num</button>
</template>
<script>
export default {
data() {
return {
num1: 1,
num2: 2,
};
},
computed: {
result() {
console.log("computed");
return this.num1 + this.num2;
},
},
methods: {
changeNum() {
this.num1++;
},
},
};
</script>
在上面的例子中,我们使用:num1作为组件的key,这样当num1变化时,Vue会重新渲染组件,computed属性也会重新计算。
4.3 手动调用$forceUpdate方法
$forceUpdate方法会强制组件重新渲染,在重新渲染时,computed属性也会重新计算。
<template>
<div>{{ result }}</div>
<button @click="changeNum">Change Num</button>
</template>
<script>
export default {
data() {
return {
num1: 1,
num2: 2,
};
},
computed: {
result() {
console.log("computed");
return this.num1 + this.num2;
},
},
methods: {
changeNum() {
this.num1++;
this.$forceUpdate();
},
},
};
</script>
在上面的例子中,当num1的值发生变化时,我们手动调用了$forceUpdate方法,这会强制组件重新渲染,在重新渲染时,computed属性也会重新计算。
五、小结
本文深入探讨了在Vue中使用computed属性时出现的computed里面的数据打印出来变化了,但是视图里面没有变化的问题。我们从computed属性的计算过程、computed属性不更新的原因和解决方法三个方面进行了详细的阐述。最终,我们提出了使用watch监听属性变化、使用key让Vue重新渲染组件和手动调用$forceUpdate方法三种解决方法。