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()
}
相关推荐
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 Canvas 基础以及动画入门
前端·html·html5
知了学历孙老师4 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
ShineWinsu6 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
福兮说7 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
程序员老赵8 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot8 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot8 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI9 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance9 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写9 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端