What Causes This Warning?
This warning occurs when you pass an invalid or misspelled option to Vue’s watch function. Only specific options like immediate, deep, flush, and once are valid.
The Problem
// ❌ Invalid option names
watch(source, callback, {
immedaite: true, // Typo!
depp: true, // Typo!
lazy: true, // Not a valid option
sync: true // Not a valid option (use flush: 'sync')
})
The Fix
Use Valid Options
// ✅ Correct option names
watch(source, callback, {
immediate: true, // Run callback immediately
deep: true, // Deep watch for nested changes
flush: 'post', // Timing: 'pre' | 'post' | 'sync'
once: true // Only trigger once (Vue 3.4+)
})
Valid Watch Options
| Option | Type | Default | Description |
|---|---|---|---|
immediate | boolean | false | Run callback immediately on creation |
deep | boolean | false | Deep watch for nested changes |
flush | 'pre' | 'post' | 'sync' | 'pre' | Callback timing |
once | boolean | false | Only trigger once (3.4+) |
onTrack | function | - | Debug: when dependency is tracked |
onTrigger | function | - | Debug: when callback is triggered |
Common Scenarios
Immediate Execution
// ❌ Wrong
watch(count, handler, { now: true })
// ✅ Correct
watch(count, handler, { immediate: true })
Deep Watching
const user = ref({ name: '', profile: { age: 0 } })
// ❌ Wrong
watch(user, handler, { recursive: true })
// ✅ Correct
watch(user, handler, { deep: true })
// Or watch specific path
watch(() => user.value.profile.age, handler)
Flush Timing
// ❌ Wrong
watch(source, handler, { sync: true })
watch(source, handler, { post: true })
// ✅ Correct
watch(source, handler, { flush: 'sync' }) // Synchronous
watch(source, handler, { flush: 'post' }) // After DOM update
watch(source, handler, { flush: 'pre' }) // Before DOM update (default)
// Shorthand for post flush
watchPostEffect(() => {
// Runs after DOM updates
})
// Shorthand for sync flush
watchSyncEffect(() => {
// Runs synchronously
})
One-Time Watch
// ❌ Wrong (Vue 3.4+)
watch(source, handler, { single: true })
// ✅ Correct
watch(source, handler, { once: true })
// Or manually stop
const stop = watch(source, (value) => {
// Do something
stop() // Stop watching
})
Options API Watch
export default {
watch: {
// ❌ Wrong option in Options API
count: {
handler(newVal) { /* ... */ },
immedaite: true // Typo!
},
// ✅ Correct
count: {
handler(newVal, oldVal) {
console.log('Count changed:', newVal)
},
immediate: true,
deep: true
}
}
}
Debugging Options
// ✅ Debug options (development only)
watch(source, callback, {
onTrack(e) {
console.log('Tracking:', e)
},
onTrigger(e) {
console.log('Triggered:', e)
}
})
watchEffect vs watch
// watchEffect - no options for source
watchEffect(() => {
console.log(count.value)
}, {
flush: 'post', // ✅ Valid
onTrack: (e) => {} // ✅ Valid
})
// watch - has source, supports all options
watch(count, (newVal) => {
console.log(newVal)
}, {
immediate: true, // ✅ Valid
deep: true, // ✅ Valid (for refs/reactive)
flush: 'post', // ✅ Valid
once: true // ✅ Valid (Vue 3.4+)
})
TypeScript
import { watch, WatchOptions } from 'vue'
const options: WatchOptions = {
immediate: true,
deep: true,
flush: 'post'
}
watch(source, callback, options)
Quick Checklist
- Check spelling:
immediatenotimmedaite - Use
deepnotrecursiveordepp - Use
flush: 'sync'notsync: true - Use
oncefor one-time watchers (Vue 3.4+) - Only
flush,onTrack,onTriggerwork withwatchEffect - Use TypeScript for option validation