题目:
看到这个题,点击这个切换子组件的按钮,销毁Child
组件之后,我们再次去点击会发现这个时候 count
还是一直往上累加的,那么这到底是为什么呢?
javascript
onMounted(() => {
timer.value = window.setInterval(() => {
count.value++;
}, 1000);
});
vue
<template>
<div>
<Child v-if="visible" />
<p>
<button @click="toggle">Toggle Child Component</button>
</p>
</div>
</template>
可以看到上面就是 count
递增的核心语法,那么再来分析,为什么点击切换子组件,由于用 v-if
指令控制,组件已经销毁了,再渲染的时候不是从上次销毁的数字继续,而是更大的数字,这表明组件销毁后,count
仍然在增加,这显然不是我们要的一个效果。
分析:
凡事必有因,别急先让我们来看几个概念。
首先看到vue官方文档的生命周期一节,onMounted
表示组件挂载完之后执行。
再看到MDN文档对setInterval
这个方法的介绍,setInterval
,在一定的时间间隔内会不停地调用函数,直到 clearInterval()
被调用或窗口被关闭。由 setInterval()
返回的 ID值可用作 clearInterval()
方法的参数。那么这里的问题就是我们通过setInterval
创建了一个定时器,却没有执行 clearInterval()
来停止它 。
最后看到我们的题目:
javascript
// Child.vue
<script setup lang="ts">
import { onMounted, onUnmounted, inject } from "vue"
const timer = inject("timer")
const count = inject("count")
onMounted(() => {
timer.value = window.setInterval(() => {
count.value++
}, 1000)
})
</script>
<template>
<div>
<p>
Child Component: {{ count }}
</p>
</div>
</template>
javascript
// App.vue
<script setup lang="ts">
import { ref, provide } from "vue"
import Child from "./Child.vue"
const visible = ref(true)
const timer = ref(null)
const count = ref(0)
provide("timer", timer)
provide("count", count)
function toggle() {
visible.value = !visible.value
}
</script>
<template>
<div>
<Child v-if="visible" />
<p>
<button @click="toggle">
Toggle Child Component
</button>
</p>
</div>
</template>
每次我们点击切换按钮的时候,子组件的onMounted
钩子触发,就会在一秒后新建一个定时器,当我们快速点击toggle
按钮多次后发现,这个count
值增加得更快了,这到底是为什么呢?这是因为子组件反复挂载和销毁,可能会引发计时器的累积问题。每次子组件挂载时,都会创建一个新的计时器并启动,但之前的计时器并没有被清除,这会导致多个定时器并行运行,自然这个count
的值也就新增多次也就是更快了。
解决:
点击切换按钮后,子组件销毁,那么也就会触发 onUnmounted
事件,尝试把停止时间的逻辑放到这里看看。
javascript
onUnmounted(() => {
window.clearInterval(timer.value);
});
这里就是每次销毁组件的时候,同时销毁创建的定时器,再次切换按钮运行,发现是可行的,在销毁期间 count
就不会再新增了。