Porque cuando observas en profundidad un objeto reactivo con watch y mutas una propiedad anidada, tanto newValue como oldValue apuntan a la misma referencia de objeto. Vue no clona el objeto antes de la mutación, por lo que cuando se ejecuta el callback, ambas referencias reflejan el estado actual (mutado).
const state = reactive({
user: { name: 'John', theme: 'dark' }
})
watch(
() => state.user,
(newUser, oldUser) => {
console.log(newUser === oldUser) // true, mismo objeto
console.log(oldUser.name) // 'Jane', no 'John'
},
{ deep: true }
)
state.user.name = 'Jane'Tanto newUser como oldUser son { name: 'Jane', theme: 'dark' }. No puedes compararlos.
Cómo solucionarlo
Opción 1: Observa la propiedad específica que te interesa. Los primitivos se copian, por lo que old/new funcionan correctamente.
watch(
() => state.user.name,
(newName, oldName) => {
console.log(`${oldName} → ${newName}`) // 'John → Jane'
}
)Opción 2: Observa varias propiedades específicas.
watch(
[() => state.user.name, () => state.user.theme],
([newName, newTheme], [oldName, oldTheme]) => {
if (newName !== oldName) console.log('name changed')
if (newTheme !== oldTheme) console.log('theme changed')
}
)Opción 3: Devuelve un clon superficial desde el getter para que Vue capture una instantánea.
watch(
() => ({ ...state.user }),
(newUser, oldUser) => {
// ahora son objetos distintos
console.log(oldUser.name, '→', newUser.name)
},
{ deep: true }
)Cuándo old y new SÍ difieren
Difieren cuando el objeto entero se reemplaza, no cuando se muta:
state.user = { name: 'Jane', theme: 'light' } // reemplazo → referencias distintas
state.user.name = 'Jane' // mutación → misma referenciaVer también: ¿Cuál es la diferencia entre watch y watchEffect? · ¿Qué es el problema de identidad del proxy reactivo?