Skip to content

监听属性 watch

监听属性是Vue3中的一个重要特性,它允许你监听响应式数据的变化,并在数据变化时执行相应的操作。本章节将详细介绍Vue3中监听属性的使用方法和最佳实践。

什么是监听属性?

监听属性是Vue3中用于监听响应式数据变化的特性,当数据发生变化时,它会执行你指定的回调函数。

监听属性的基本使用

1. 监听单个数据

在Vue3的组合式API中,我们使用 watch 函数来监听数据变化:

vue
<template>
  <div>
    <input v-model="message" type="text">
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const message = ref('Hello')

watch(message, (newValue, oldValue) => {
  console.log(`Message changed from ${oldValue} to ${newValue}`)
})
</script>

2. 监听多个数据

你可以同时监听多个数据的变化:

vue
<template>
  <div>
    <input v-model="message" type="text">
    <input v-model="count" type="number">
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const message = ref('Hello')
const count = ref(0)

watch([message, count], ([newMessage, newCount], [oldMessage, oldCount]) => {
  console.log(`Message changed from ${oldMessage} to ${newMessage}`)
  console.log(`Count changed from ${oldCount} to ${newCount}`)
})
</script>

3. 监听对象

你可以监听对象的变化:

vue
<template>
  <div>
    <input v-model="user.name" type="text">
    <input v-model="user.age" type="number">
  </div>
</template>

<script setup>
import { reactive, watch } from 'vue'

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

watch(user, (newUser, oldUser) => {
  console.log('User changed:', newUser, oldUser)
})
</script>

监听属性的配置选项

1. deep 选项

默认情况下,watch 只监听对象的引用变化,不会监听对象内部属性的变化。如果要监听对象内部属性的变化,需要使用 deep 选项:

vue
<template>
  <div>
    <input v-model="user.name" type="text">
    <input v-model="user.age" type="number">
  </div>
</template>

<script setup>
import { reactive, watch } from 'vue'

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

watch(user, (newUser, oldUser) => {
  console.log('User changed:', newUser, oldUser)
}, { deep: true })
</script>

2. immediate 选项

默认情况下,watch 只在数据变化时执行回调函数。如果要在监听开始时就执行一次回调函数,需要使用 immediate 选项:

vue
<template>
  <div>
    <input v-model="message" type="text">
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const message = ref('Hello')

watch(message, (newValue, oldValue) => {
  console.log(`Message changed from ${oldValue} to ${newValue}`)
}, { immediate: true })
</script>

3. flush 选项

flush 选项用于控制回调函数的执行时机:

  • pre:在组件更新之前执行(默认)
  • post:在组件更新之后执行
  • sync:同步执行
vue
<template>
  <div>
    <input v-model="message" type="text">
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const message = ref('Hello')

watch(message, (newValue, oldValue) => {
  console.log(`Message changed from ${oldValue} to ${newValue}`)
}, { flush: 'post' })
</script>

watchEffect

watchEffect 是Vue3中另一个用于监听数据变化的API,它会自动追踪响应式数据的变化:

vue
<template>
  <div>
    <input v-model="message" type="text">
    <input v-model="count" type="number">
  </div>
</template>

<script setup>
import { ref, watchEffect } from 'vue'

const message = ref('Hello')
const count = ref(0)

watchEffect(() => {
  console.log(`Message: ${message.value}, Count: ${count.value}`)
})
</script>

watchEffect vs watch

  • watchEffect

    • 自动追踪响应式数据的变化
    • 立即执行一次回调函数
    • 无法获取旧值
    • 更简洁,适合简单的场景
  • watch

    • 需要明确指定要监听的数据
    • 默认只在数据变化时执行
    • 可以获取旧值
    • 更灵活,适合复杂的场景

监听属性的使用场景

1. 数据变化时执行副作用操作

vue
<template>
  <div>
    <input v-model="search" type="text" placeholder="Search">
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const search = ref('')

watch(search, (newSearch) => {
  // 当搜索词变化时,发送请求获取数据
  fetch(`https://api.example.com/search?q=${newSearch}`)
    .then(response => response.json())
    .then(data => {
      console.log(data)
    })
})
</script>

2. 表单验证

vue
<template>
  <div>
    <input v-model="email" type="email" placeholder="Email">
    <p v-if="error" style="color: red;">{{ error }}</p>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const email = ref('')
const error = ref('')

watch(email, (newEmail) => {
  if (!newEmail) {
    error.value = 'Email is required'
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(newEmail)) {
    error.value = 'Invalid email format'
  } else {
    error.value = ''
  }
})
</script>

3. 数据持久化

vue
<template>
  <div>
    <input v-model="message" type="text">
  </div>
</template>

<script setup>
import { ref, watch, onMounted } from 'vue'

const message = ref('')

// 组件挂载时从本地存储获取数据
onMounted(() => {
  const savedMessage = localStorage.getItem('message')
  if (savedMessage) {
    message.value = savedMessage
  }
})

// 数据变化时保存到本地存储
watch(message, (newMessage) => {
  localStorage.setItem('message', newMessage)
})
</script>

4. 计算属性的替代方案

当计算属性需要执行异步操作时,可以使用 watch 替代:

vue
<template>
  <div>
    <input v-model="userId" type="number">
    <p v-if="loading">Loading...</p>
    <p v-else-if="error">{{ error }}</p>
    <p v-else>{{ user.name }}</p>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const userId = ref(1)
const user = ref(null)
const loading = ref(false)
const error = ref('')

watch(userId, async (newUserId) => {
  loading.value = true
  error.value = ''
  try {
    const response = await fetch(`https://api.example.com/users/${newUserId}`)
    if (!response.ok) {
      throw new Error('User not found')
    }
    user.value = await response.json()
  } catch (err) {
    error.value = err.message
  } finally {
    loading.value = false
  }
}, { immediate: true })
</script>

监听属性的最佳实践

1. 合理使用深度监听

深度监听会遍历对象的所有属性,可能会影响性能,因此应该只在必要时使用:

javascript
// 好的做法:只监听需要的属性
watch(() => user.name, (newName, oldName) => {
  console.log(`Name changed from ${oldName} to ${newName}`)
})

// 不好的做法:深度监听整个对象
watch(user, (newUser, oldUser) => {
  console.log('User changed:', newUser, oldUser)
}, { deep: true })

2. 清理副作用

当监听属性执行的操作需要清理时,应该在回调函数中返回一个清理函数:

javascript
watch(search, (newSearch) => {
  // 发送请求前取消之前的请求
  const controller = new AbortController()
  
  fetch(`https://api.example.com/search?q=${newSearch}`, {
    signal: controller.signal
  })
    .then(response => response.json())
    .then(data => {
      console.log(data)
    })
  
  // 返回清理函数
  return () => {
    controller.abort()
  }
})

3. 避免在回调函数中修改被监听的数据

在监听属性的回调函数中修改被监听的数据可能会导致无限循环:

javascript
// 不好的做法:在回调函数中修改被监听的数据
watch(count, (newCount) => {
  // 这会导致无限循环
  count.value = newCount + 1
})

4. 合理使用 watchEffect

对于简单的场景,使用 watchEffect 可以使代码更简洁:

javascript
// 使用 watchEffect
watchEffect(() => {
  localStorage.setItem('message', message.value)
})

// 等价于
watch(message, (newMessage) => {
  localStorage.setItem('message', newMessage)
}, { immediate: true })

总结

监听属性是Vue3中的一个重要特性,它允许你监听响应式数据的变化,并在数据变化时执行相应的操作。通过 watchwatchEffect,你可以实现数据变化时的副作用操作、表单验证、数据持久化等功能。

在使用监听属性时,你应该合理使用深度监听,清理副作用,避免在回调函数中修改被监听的数据,以及根据场景选择合适的监听API。

在后续的章节中,我们将学习Vue3的样式绑定,以及它的使用方法和最佳实践。

© 2026 编程马·菜鸟教程 版权所有