vue3 watchEffect 自动卸载时机引发的疑问?
个人背景: 一枚前端开发
问题场景: 在开发的时候,有这样一个需求,有一个类似于进度条进度的数据 mockData,A,B,页面都需要使用到这个数据,所以我选择把它放到全局的 pinia 里管理。并且在 A 页面用户点击按钮的时候,开始观察 mockData 的变化。(我这里选择使用了 wathcEffect)
场景模拟:
这是 mockData
import { ref } from "vue";
const count = ref(0); //全局的进度条 mockData
setInterval(() => {
count.value++; // 不停 +1
console.log("count.value", count.value);
}, 1000);
export { count };
这是 A 页面的代码, B 页面没有什么重要的代码,只是作为一个路由跳转到的界面而已。(无需关系 B 页面做了什么)
import { watchEffect } from "vue";
import { useRouter } from "vue-router";
import { count } from "@/count++";
const router = useRouter();
function test() {
watchEffect(() => {
const a = count.value;
console.log("a", a);
if (a === 10) {
router.replace("/red");
}
});
}
</script>
<template>
<div class="w-full h-full centered bg-blue">
<button @click="test" for="test1" class="w-100px h-100px bg-gray">
click
</button>
</div>
</template>
- 我进到 A 页面点击按钮,开始观察 mockData 的变化,当 a 到10的时候,页面跳转 到 B 页面。
- 此时错误的预期发生了,页面到了 B ,但是对 a 的打印还在输出。
- 我将 watchEffect 移出函数,则一切正常
问题: 为什么会导致这样的现象呢?为什么页面都消失了,但是对于 a 的watch 还没停止。
回答:
首先你这里存在一个误解,watchEffect
不是和页面产生的绑定关系,而是传入 watchEffect
的回调函数与函数中用到的响应式状态
产生的绑定关系,也就是所谓的依赖搜集
关键在这里,你定义了一个无法回收的 setInterval
,页面拉起来后将一直运行,而你 A -> B 只是单纯的前端路由跳转,本质还是同一个页面,所以不会影响 setInterval
持续运行, setInterval
没有被清理掉,count
就会一直变,继而 watchEffect
的回调也会一直执行
setInterval(() => { count.value++; // 不停 +1
console.log("count.value", count.value);
}, 1000);
要解决这个现象么在跳转路由前清理掉 setInterval
,要么 stop 掉 watchEffect
, 但按照你这种用法,两个都应该清理,因为这里 watchEffect
的回调引用的是一个全局的响应式状态,不手动 stop 掉的话,跳转路由后该回调函数所占用的内存是无法被回收的
另外,还有一个细节,同步的 watchEffect
会挂在组件实例上,随组件销毁而销毁;而异步创建的需要手动清理,具体看官方文档
回答:
<script setup lang="ts">import { watchEffect, onBeforeUnmount } from "vue";
import { useRouter } from "vue-router";
import { count } from "@/count++";
const router = useRouter();
let stopWatchEffect;
function test() {
stopWatchEffect = watchEffect(() => {
const a = count.value;
console.log("a", a);
if (a === 10) {
router.replace("/red");
// 跳转了路由,就停止 watchEffect
if (stopWatchEffect) stopWatchEffect();
}
});
}
// 用生命周期清理
onBeforeUnmount(() => {
if (stopWatchEffect) stopWatchEffect();
});
</script>
<template>
<div class="w-full h-full centered bg-blue">
<button @click="test" class="w-100px h-100px bg-gray">
click
</button>
</div>
</template>
回答:
https://cn.vuejs.org/guide/essentials/watchers.html#stopping-...
以上是 vue3 watchEffect 自动卸载时机引发的疑问? 的全部内容, 来源链接: utcz.com/p/935145.html