Vue3-watchEffect

watchEffect 是 Vue 3 组合式 API 提供的另一个侦听器。它和 watch 目标一致(在数据变化时执行副作用),但使用方式更简洁,因为它会自动追踪依赖

1.核心理念

watchEffect 的核心理念是: "立即执行一次,并自动追踪函数内部用到的所有响应式数据,在它们变化时重新执行。"

它只需要一个参数:一个函数。

ts 复制代码
<template>
  <p>User ID: {{ userId }}</p>
  <button @click="changeUser">切换用户</button>
</template>

<script setup lang="ts">
import { ref, watchEffect } from 'vue';

const userId = ref(1);

// 1. 立即执行:
// watchEffect 会在 <script setup> 执行到这里时立即运行一次
// 它会自动"发现"内部用到了 userId.value
watchEffect(() => {
  // 当 watchEffect 运行时,它会追踪所有被"读取"的 .value
  console.log(`(watchEffect) 正在获取 ID: ${userId.value} 的数据...`);
  
  // 假设的 API 调用
  // fetchUserData(userId.value);
});

// 2. 自动追踪变化:
// 当 userId 变化时,上面的函数会 *自动* 重新运行
const changeUser = () => {
  userId.value++;
};
</script>

2.watchEffectwatch 的核心区别

特性 watch watchEffect
依赖源 手动指定 (必须明确告诉它侦听谁) 自动追踪 (它会侦听函数体内部用到的所有数据)
立即执行 默认不会 (需配置 { immediate: true }) 默认立即执行
访问旧值 可以 (回调参数 (newVal, oldVal)) 不可以 (回调不接收任何参数)
侧重点 适合精确控制 更"轻量级",适合简单的、自动化的副作用

3.watchEffect 的高级用法

  • 停止侦听:watchEffect 同样会返回一个"停止句柄" (Stop Handle) 函数。

<script setup> 中,它也会自动绑定到组件生命周期,并在组件卸载时自动停止,所以你通常不需要手动停止。

ts 复制代码
import { watchEffect } from 'vue';

const stopHandle = watchEffect(() => {
  // ...
});

// 在未来的某个时刻
stopHandle(); // 停止这个 effect
  • 清除副作用 (onInvalidate):watchEffect 的回调函数可以接收一个 onInvalidate 函数作为参数。这个函数用于注册一个"失效"时的回调。

watchEffect 即将重新运行 时(或在侦听器被停止 时),onInvalidate 注册的回调会先执行。这在处理异步操作竞态时非常有用(例如,短时间内多次触发 API 请求)。

举个栗子

ts 复制代码
<template>
  <div>
    <h3>onInvalidate</h3>
    <p>在控制台中查看日志,并快速输入:</p>
    <input v-model="query" />
    <p>当前查询: {{ query }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref, watchEffect } from 'vue';

const query = ref('vue');

watchEffect((onInvalidate) => {
  // 1. 创建 AbortController
  const controller = new AbortController();
  const { signal } = controller;

  console.log(`(Fetch) 正在搜索: ${query.value}`);
  
  // 2. 使用 Open Library 的 API
  // 对 URL 进行了编码 (encodeURIComponent) 以处理特殊字符
  const url = `https://openlibrary.org/search.json?q=${encodeURIComponent(query.value)}`;

  fetch(url, { signal })
    .then(response => response.json()) // 将响应解析为 JSON
    .then(data => {
      // 3. 成功获取数据
      console.log(`(Success) 成功获取: ${query.value}`, data.docs.length, '条结果');
    })
    .catch(err => {
      // 4. 捕获错误
      if (err.name === 'AbortError') {
        //  预期的中止错误
        console.log(`(Abort) 已取消上一次请求: ${query.value}`);
      } else {
        // 其他网络错误
        console.error('Fetch 错误:', err);
      }
    });

  // 5. 注册"失效"回调
  onInvalidate(() => {
    console.log('...查询变化太快,Effect 即将失效,中止上一个请求...');
    // 6. 中止上一次的 fetch 请求
    controller.abort();
  });
});

</script>

当快速输入333时,控制台打印

检查网络,前两次的快速请求都被取消了,只有最后一次成功

4.总结

onInvalidate 适用于任何有"清理"需求的副作用

  1. 取消异步请求 :(最常用) fetch, axios 等。
  2. 清除定时器 :清除 setTimeoutsetInterval
  3. 解绑事件监听器 :如果在 watchEffect 内部用 window.addEventListener 动态添加了一个事件,你可以在 onInvalidate 中用 window.removeEventListener 将其移除,防止内存泄漏。

两个关键区别:

  • 防抖/节流 (Debounce/Throttle) (用 watch):

    • 目标:减少执行次数
    • 逻辑:"等一等再执行"。
  • 清理副作用 (Cleanup) (用 watchEffect + onInvalidate):

    • 目标:防止旧操作干扰新操作
    • 逻辑:"开始新的之前,先确保旧的已经停止了"。
相关推荐
Pedantic1 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘1 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆1 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师2 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆2 小时前
VSCode自动格式化三要素
前端
爱勇宝3 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen4 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user20585561518136 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode6 小时前
Redis 在生产项目的使用
前端·后端