学习笔记--vue3 watchEffect监听的各种姿势用法和总结

watchEffect 监听不同数据源

watchEffect 会自动追踪在其回调函数中使用的所有响应式依赖,无需显式指定数据源。

1. 监听单个 ref

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

const count = ref(0)

// 自动追踪 count
watchEffect(() => {
  console.log('count 值:', count.value)
  // 当 count 变化时自动执行
})

// 修改值会触发
count.value++ // 输出: count 值: 1

2. 监听多个 ref

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

const count = ref(0)
const name = ref('John')
const age = ref(18)

// 自动追踪所有使用的 ref
watchEffect(() => {
  console.log(`姓名: ${name.value}, 年龄: ${age.value}, 计数: ${count.value}`)
  // 当 name、age 或 count 任何一个变化时都会执行
})

// 任何修改都会触发
count.value++  // 触发
name.value = 'Jane'  // 触发
age.value = 20  // 触发

3. 监听单个 reactive

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

const state = reactive({
  count: 0,
  name: 'John'
})

// 方式1:直接使用整个对象(会深度追踪所有属性)
watchEffect(() => {
  console.log('state 整体:', state)
  // 当 state 的任何属性变化时都会触发
})

// 方式2:只追踪特定属性(性能更好)
watchEffect(() => {
  console.log('count 值:', state.count)
  // 只有当 state.count 变化时才触发
})

// 修改会触发
state.count++  // 触发方式1和方式2
state.name = 'Jane'  // 只触发方式1

4. 监听多个 reactive

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

const user = reactive({
  name: 'John',
  age: 18
})

const settings = reactive({
  theme: 'dark',
  language: 'zh'
})

// 自动追踪所有使用的 reactive 属性
watchEffect(() => {
  console.log(`用户: ${user.name}, ${user.age}岁`)
  console.log(`设置: ${settings.theme}主题, ${settings.language}语言`)
  // 当 user.name、user.age、settings.theme、settings.language 任一变化时触发
})

// 修改会触发
user.name = 'Jane'  // 触发
settings.theme = 'light'  // 触发

5. 混合监听 ref 和 reactive

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

const count = ref(0)
const user = reactive({
  name: 'John',
  info: {
    age: 18,
    city: 'Beijing'
  }
})

// 自动追踪所有使用的响应式数据
watchEffect(() => {
  console.log(`计数: ${count.value}`)
  console.log(`用户: ${user.name}`)
  console.log(`年龄: ${user.info.age}`)
  console.log(`城市: ${user.info.city}`)
  // 依赖:count.value、user.name、user.info.age、user.info.city
})

// 任何依赖变化都会触发
count.value++  // 触发
user.name = 'Jane'  // 触发
user.info.age = 20  // 触发
user.info.city = 'Shanghai'  // 触发

6. 监听嵌套 reactive 对象

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

const state = reactive({
  user: {
    profile: {
      name: 'John',
      address: {
        city: 'Beijing',
        street: 'Main St'
      }
    }
  }
})

// 深度追踪:会自动追踪所有访问的嵌套属性
watchEffect(() => {
  console.log('城市:', state.user.profile.address.city)
  console.log('街道:', state.user.profile.address.street)
  // 只追踪 city 和 street 的变化
})

// 修改嵌套属性会触发
state.user.profile.address.city = 'Shanghai'  // 触发
state.user.profile.address.street = 'Nanjing Rd'  // 触发

// 修改未追踪的属性不会触发
state.user.profile.name = 'Jane'  // 不会触发(未在回调中使用)

7. watchEffect 的清理和停止

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

const count = ref(0)

// watchEffect 返回停止函数
const stop = watchEffect((onCleanup) => {
  console.log('count:', count.value)
  
  // 清理函数:在重新运行前或停止时执行
  onCleanup(() => {
    console.log('清理副作用')
    // 用于取消请求、清除定时器等
  })
})

// 停止监听
stop()

8. 异步 watchEffect

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

const id = ref(1)
const data = ref(null)

watchEffect(async (onCleanup) => {
  let cancelled = false
  
  onCleanup(() => {
    cancelled = true
  })
  
  // 模拟异步请求
  const response = await fetch(`/api/data/${id.value}`)
  if (!cancelled) {
    data.value = await response.json()
  }
})

9. 控制执行时机

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

const count = ref(0)

// flush: 'pre' (默认) - 组件更新前执行
watchEffect(() => {
  console.log('pre:', count.value)
}, {
  flush: 'pre'
})

// flush: 'post' - 组件更新后执行
watchEffect(() => {
  console.log('post:', count.value)
}, {
  flush: 'post'
})

// flush: 'sync' - 同步执行
watchEffect(() => {
  console.log('sync:', count.value)
}, {
  flush: 'sync'
})

10. watchEffect vs watch 对比

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

const count = ref(0)
const state = reactive({ name: 'John', age: 18 })

// watch: 显式指定数据源
watch(count, (newVal, oldVal) => {
  console.log('watch - count:', newVal, oldVal)
})

watch(
  [() => state.name, () => state.age],
  ([newName, newAge], [oldName, oldAge]) => {
    console.log('watch - name/age:', newName, newAge)
  }
)

// watchEffect: 自动追踪依赖
watchEffect(() => {
  console.log('watchEffect - count:', count.value)
  console.log('watchEffect - name/age:', state.name, state.age)
  // 自动追踪 count.value、state.name、state.age
})

// 执行时机
// watch: 懒执行,只有数据变化时才执行
// watchEffect: 立即执行一次,然后依赖变化时执行

实际应用示例

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

// 用户搜索示例
const searchKeyword = ref('')
const filters = reactive({
  category: 'all',
  sortBy: 'date',
  priceRange: [0, 1000]
})
const results = ref([])

// 自动搜索:任何搜索条件变化时自动执行
watchEffect(async () => {
  console.log('搜索条件变化,重新获取数据')
  
  // 构建查询参数
  const params = {
    keyword: searchKeyword.value,
    category: filters.category,
    sortBy: filters.sortBy,
    minPrice: filters.priceRange[0],
    maxPrice: filters.priceRange[1]
  }
  
  // 模拟 API 请求
  const response = await fetch(`/api/search?${new URLSearchParams(params)}`)
  results.value = await response.json()
})

// 任何条件变化都会触发搜索
searchKeyword.value = 'vue'  // 触发搜索
filters.category = 'books'   // 触发搜索
filters.sortBy = 'rating'    // 触发搜索
filters.priceRange = [0, 500] // 触发搜索

总结对比

特性 watch watchEffect
数据源 显式指定 自动追踪依赖
执行时机 懒执行(首次不执行) 立即执行
旧值获取 ✅ 可以获取 ❌ 无法获取
监听多个 需要数组 自动收集
嵌套对象 需要 deep 选项 自动深度追踪(访问到的属性)
性能优化 更精确控制 自动优化

最佳实践

  1. 使用 watchEffect 当

    • 不需要获取旧值
    • 依赖关系简单且自动
    • 需要立即执行副作用
  2. 使用 watch 当

    • 需要获取旧值
    • 需要精确控制监听的数据源
    • 需要懒执行(首次不执行)
  3. 性能优化

    javascript 复制代码
    // ❌ 避免:访问过多属性导致频繁执行
    watchEffect(() => {
      console.log(state)  // 任何属性变化都触发
    })
    
    // ✅ 推荐:只访问需要的属性
    watchEffect(() => {
      console.log(state.name, state.age)  // 只有这些属性变化才触发
    })

# 学习笔记--vue3 watch监听的各种姿势用法和总结

相关推荐
你挚爱的强哥2 小时前
解决:动态文本和背景色一致导致文字看不清楚,用js获取背景图片主色调,并获取对比度最大的hex色值给文字
前端·javascript·github
用户69371750013842 小时前
Android 手机终于能当电脑用了
android·前端
wooyoo2 小时前
花了一周 vibe 了一个 OpenClaw 的 Agent 市场,聊聊过程中踩的坑
前端·后端·agent
angerdream2 小时前
最新版vue3+TypeScript开发入门到实战教程之路由详解
前端·javascript·vue.js
送鱼的老默2 小时前
学习笔记--vue3 watch监听的各种姿势用法和总结
前端·vue.js
猪八宅百炼成仙2 小时前
解决 el-date-picker type:daterange 在 layout 布局中的宽度问题
前端·element
小贺要学前端3 小时前
ES6 还没用明白,JavaScript 已经快到 ES2026 了
前端·javascript·es6
Amumu121383 小时前
JS:ES6~ES11基础语法(二)
开发语言·前端·javascript
Amumu121383 小时前
Js:ES6~ES11基础语法(一)
开发语言·前端·javascript