vue3 watchEffect 自动卸载时机引发的疑问?

个人背景: 一枚前端开发

问题场景: 在开发的时候,有这样一个需求,有一个类似于进度条进度的数据 mockData,A,B,页面都需要使用到这个数据,所以我选择把它放到全局的 pinia 里管理。并且在 A 页面用户点击按钮的时候,开始观察 mockData 的变化。(我这里选择使用了 wathcEffect)

场景模拟:

  1. 这是 mockData

    import { ref } from "vue";

    const count = ref(0); //全局的进度条 mockData

    setInterval(() => {

    count.value++; // 不停 +1

    console.log("count.value", count.value);

    }, 1000);

    export { count };

  2. 这是 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>

  3. 我进到 A 页面点击按钮,开始观察 mockData 的变化,当 a 到10的时候,页面跳转 到 B 页面。
    vue3 watchEffect 自动卸载时机引发的疑问?
  4. 此时错误的预期发生了,页面到了 B ,但是对 a 的打印还在输出。
    vue3 watchEffect 自动卸载时机引发的疑问?
  5. 我将 watchEffect 移出函数,则一切正常
    vue3 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 会挂在组件实例上,随组件销毁而销毁;而异步创建的需要手动清理,具体看官方文档
vue3 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

回到顶部