vue3中的watchEffect()监听,什么时候监听,什么时候清理,什么时候停止

先写一个vue3的完整示例,取名为home.vue

javascript 复制代码
<script setup>
import { ref,watchEffect,onMounted,onUnmounted} from 'vue'
const count=ref(0);
const userid=ref(1);
const userData=ref(null);
const timer=ref(0);

//同步监听,只是读取数据或是计算属性没有必要进行清理
watchEffect(()=>{
    console.log(`当前count:${count.value},userid:${userid.value}`);
})
//异步获取数据,如获取后端数据,或是有定时器之类的setTimeout(),便要做清理
watchEffect(async (onInvalidate)=>{
    const id=userid.value;
    console.log(`开始获取用户${id}的数据...`);
    //模拟异步请求
    const controller=new AbortController();	   //创建一个"取消控制器"
    try{
        const timeoutid=setTimeout(()=>{
            userData.value={id,username:`用户${id}`}
            console.log(`用户${id}数据获取完成`)
        },1000);

        //清理函数:在effect重新执行前或停止时调用
        onInvalidate(()=>{
            clearTimeout(timeoutid);
            controller.abort()			//如果是fetch获取后端数据或定时器,立刻中断
            console.log(`清理用户${id}的pending请求`)
        })
    }catch(e)
    {
        if(e.name==='AbortError')
        {
            console.log('请示被取消');
        }
    }
})

//手动停止watchEffect
let stopTimerWatch;

onMounted(()=>{
    stopTimerWatch=watchEffect(()=>{
        console.log(`计时器:${timer.value}`)
    })
})
onUnmounted(() => {
  // 组件卸载时手动停止,防止内存泄漏
  stopTimerWatch && stopTimerWatch()
  console.log('timer 的 watchEffect 已停止')
})


//辅助函数
const increment=()=>count.value++;
const changeuser=()=>userid.value++;
const startTimer=()=>{
    const interval = setInterval(() => timer.value++, 1000)
    // 5秒后停止 timer 的 watchEffect
    setTimeout(() => {
        stopTimerWatch && stopTimerWatch()
        clearInterval(interval)
        console.log('手动停止了 timer 监听')
    }, 5000)
}
</script>
<template>
    
    <div>
        <h4>count:{{count}}</h4>
        <button @click="increment">+1</button>
        <h4>用户ID:{{userid}}</h4>
        <button @click="changeuser">切换用户</button>
        <p>用户数据:{{userData}}</p>
        <h4>计时器:{{timer}}</h4>
        <button @click="startTimer">开始计时(5秒后自动停止监听)</button>
    </div>
</template>
<style scoped>

</style>

不需要清理的情况

//只是纯计算/读取,没有遗留任何"副作用",所以不需要清理

javascript 复制代码
import { ref, watchEffect } from 'vue'

const count = ref(0)
const double = ref(0)

// 纯同步:只是读取 count,计算 double
// 没有产生"副作用"(定时器、请求、事件等),所以不需要 onInvalidate
watchEffect(() => {
  double.value = count.value * 2
  console.log('count 变了,double =', double.value)
})

异步需要清理的情况

javascript 复制代码
import { ref, watchEffect } from 'vue'

const keyword = ref('vue')
const results = ref([])

watchEffect(async (onInvalidate) => {
  const currentKeyword = keyword.value
  
  // 产生副作用:发起网络请求
  const controller = new AbortController()
  
  try {
    const res = await fetch(`/api/search?q=${currentKeyword}`, {
      signal: controller.signal
    })
    const data = await res.json()
    
    // ⚠️ 竞态问题:如果 keyword 快速变化,旧的请求可能后返回
    // 需要确保只采用最新请求的结果
    if (currentKeyword === keyword.value) {
      results.value = data
    }
    
  } catch (e) {
    // 忽略取消请求的错误
  }
  
  // 🔧 清理:在 effect 重新执行前,取消上一个未完成的请求
  onInvalidate(() => {
    controller.abort()
  })
})

同步也需要清理的情况

javascript 复制代码
import { ref, watchEffect } from 'vue'

const visible = ref(false)

watchEffect((onInvalidate) => {
  if (visible.value) {
    // 同步产生了副作用:添加事件监听
    const handler = (e) => console.log('点击了', e.target)
    document.addEventListener('click', handler)
    
    // 🔧 必须清理,否则监听会越积越多
    onInvalidate(() => {
      document.removeEventListener('click', handler)
    })
  }
})

终止stop使用时机

javascript 复制代码
import { ref, watchEffect, onUnmounted } from 'vue'

const count = ref(0)

const stop = watchEffect(() => {
  console.log(count.value)
})

// 情况1:组件卸载时停止,防止内存泄漏
onUnmounted(() => {
  stop()
})

// 情况2:满足某个条件后不再需要监听
if (someCondition) {
  stop()
}
相关推荐
世界哪有真情13 分钟前
AI 写代码两年多,我发现自己越来越"看不进去"了
前端·后端·ai编程
星栈15 分钟前
被 Rust async 纠正的三个异步认知
前端·后端·rust
前端_刘师兄27 分钟前
前端开发工程师转FAE工程师路线规划
前端
禁止摆烂_才浅29 分钟前
前端 AI 面试题
前端·面试·ai编程
程序员老赵31 分钟前
Docker 部署 ZLMediaKit:轻松搭建高性能流媒体服务平台
前端·javascript·后端
Liora_Yvonne33 分钟前
为什么每次发版,总有用户看到白屏?
前端
风月说与山鬼35 分钟前
四、浏览器存储
前端·vue.js
hunterandroid38 分钟前
[Android 从零到一] ViewPager2 与 Fragment 生命周期协同:从预加载到状态一致性
android·前端
cindershade40 分钟前
别盯着 dist:Vite 应用的下载与执行成本治理闭环
前端
拾年27541 分钟前
前后端分离开发,别再"傻等"后端接口了——聊聊前端接口工程
前端·javascript·react.js