# 1.watch 监视源只能是响应式对象
App 为根组件 下面 Home 例子都被此组件引用
// App.vue | |
<template> | |
<div> | |
<home></home> | |
</div> | |
</template> | |
<script> | |
import Home from "./Home.vue"; | |
export default { | |
components: { | |
Home, | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeAge">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { watch } from "vue"; | |
export default { | |
setup() { | |
const info = { name: "Neko", age: 16 }; //watch 传入不可是普通对象 | |
// 1. 侦听 watch | |
watch(info, (newValue, oldValue) => { | |
console.log("newValue:", newValue, "oldValue:", oldValue); | |
}); | |
const changeAge = () => { | |
info.age++; | |
}; | |
return { | |
info, | |
changeAge, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 2. 传入一个 getter 函数
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeAge">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { reactive, watch } from "vue"; | |
export default { | |
setup() { | |
const info = reactive({ name: "Neko", age: 16 }); | |
// 1. 侦听 watch 时,传入一个 getter 函数 | |
watch( | |
() => info.age, // getter | |
(newValue, oldValue) => { | |
// 这里使用 ref 并没有触发 watch | |
console.log("newValue:", newValue, "oldValue:", oldValue); | |
} | |
); | |
const changeAge = () => { | |
info.age++; | |
}; | |
return { | |
info, | |
changeAge, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 3. 侦听的 reactive 对象
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeAge">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { reactive, watch } from "vue"; | |
export default { | |
setup() { | |
const info = reactive({ name: "Neko", age: 16 }); | |
// 1. 传入一个可响应式对象:reactive 对象 或 ref 对象 | |
//reactive 对象获取到的 newValue 和 oldValue 本身都是 reactive 对象 | |
watch(info, (newValue, oldValue) => { | |
// 注意 newValue 和 oldValue 结果是不一样的 | |
console.log("newValue:", newValue, "oldValue:", oldValue); //newValue 和 oldValue 都是 Proxy | |
}); | |
const changeAge = () => { | |
info.age++; | |
}; | |
return { | |
info, | |
changeAge, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 4. 侦听的 ref 对象
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeInfo">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { ref, watch } from "vue"; | |
export default { | |
setup() { | |
//const info = ref ({age: 16}); // 如果改变 age 不会触发 watch | |
const info = ref("Neko"); | |
// 1. 传入一个可响应式对象:reactive 对象 或 ref 对象 | |
//ref 对象获取到的 newValue 和 oldValue 是 value 值的本身 | |
watch(info, (newValue, oldValue) => { | |
// 这里使用 ref 并没有触发 watch | |
console.log("newValue:", newValue); // 值都是 value ,不是 ref 对象 | |
console.log("oldValue:", oldValue); | |
}); | |
const changeInfo = () => { | |
//info.value.age++; // 不会触发 watch | |
info.value = "注意如果info是一个对象,则改变里面的数据不会触发 watch"; | |
}; | |
return { | |
info, | |
changeInfo, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 5. 使用 getter 对 reactive 对象为普通对象
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeAge">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { reactive, watch } from "vue"; | |
export default { | |
setup() { | |
const info = reactive({ name: "Neko", age: 16 }); | |
// 1. 传入一个可响应式对象:reactive 对象 或 ref 对象 | |
//reactive 对象获取到的 newValue 和 oldValue 本身都是 reactive 对象 | |
// 如果希望 newValue 和 oldValue 是一个普通的对象 | |
watch( | |
// 相当于变成了一个 getter, 返回了一个对象 | |
() => ({ ...info }), | |
(newValue, oldValue) => { | |
// 注意 newValue 和 oldValue 结果这次是不一样的 | |
console.log("newValue:", newValue, "oldValue:", oldValue); // 此时已经是普通的对象了 | |
} | |
); | |
const changeAge = () => { | |
info.age++; | |
}; | |
return { | |
info, | |
changeAge, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 6. 关于使用 getter 解构后不是深度监听
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeInfo">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { reactive, watch } from "vue"; | |
export default { | |
setup() { | |
const info = reactive({ | |
name: "Neko", | |
age: 16, | |
friend: { | |
name: "Nico", | |
}, | |
}); | |
// 1. 侦听 watch 时,传入一个 getter 函数 | |
watch( | |
// 使用 getter 解构后不是深度监听 | |
() => ({ ...info }), | |
(newValue, oldValue) => { | |
console.log("newValue:", newValue, "oldValue:", oldValue); | |
} | |
); | |
const changeInfo = () => { | |
info.friend.name = "Aphrodite"; // 无法深度侦听 | |
}; | |
return { | |
info, | |
changeInfo, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 7.watch 第三个参数 (对象) 中的 deep 属性
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeInfo">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { reactive, watch } from "vue"; | |
export default { | |
setup() { | |
const info = reactive({ | |
name: "Neko", | |
age: 16, | |
friend: { | |
name: "Nico", | |
}, | |
}); | |
// 1. 侦听 watch 时,传入一个 getter 函数 | |
watch( | |
// 使用 getter 解构后不是深度监听 | |
() => ({ ...info }), | |
(newValue, oldValue) => { | |
console.log("newValue:", newValue, "oldValue:", oldValue); | |
}, | |
{ | |
deep: true, // 使用 deep 深度侦听 | |
} | |
); | |
const changeInfo = () => { | |
info.friend.name = "Aphrodite"; | |
}; | |
return { | |
info, | |
changeInfo, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 8.watch 第三个参数 (对象) 中 的 immediate 属性
// Home.vue | |
<template> | |
<div> | |
<h2>info:{{ info }}</h2> | |
<button @click="changeInfo">修改年龄</button> | |
</div> | |
</template> | |
<script> | |
import { reactive, watch } from "vue"; | |
export default { | |
setup() { | |
const info = reactive({ | |
name: "Neko", | |
age: 16, | |
friend: { | |
name: "Nico", | |
}, | |
}); | |
// 1. 侦听 watch 时,传入一个 getter 函数 | |
watch( | |
// 使用 getter 解构后不是深度监听 | |
() => ({ ...info }), | |
(newValue, oldValue) => { | |
console.log("newValue:", newValue, "oldValue:", oldValue); | |
}, | |
{ | |
deep: true, // 使用 deep 深度侦听 | |
immediate: true, // 立即执行,第一次也会打印。 不过因为未发生改变,所以没有 oldValue 值 | |
} | |
); | |
const changeInfo = () => { | |
info.friend.name = "Aphrodite"; | |
}; | |
return { | |
info, | |
changeInfo, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 9. 侦听多个数据源(数组)
// Home.vue | |
<template> | |
<div> | |
<h2 ref="title">[info.name]:{{ info.name }}</h2> | |
<button @click="changeData">修改数据</button> | |
</div> | |
</template> | |
<script> | |
import { ref, reactive, watch } from "vue"; | |
export default { | |
setup() { | |
// 1. 定义可响应式的对象 | |
const info = reactive({ name: "Neko", age: 18 }); | |
const name = ref("aimer"); | |
console.log("info:", info); | |
console.log("name:", name); | |
// 2. 侦听器 watch 使用数组侦听多个数据源 | |
watch( | |
// 解构 reactive 对象 | |
[() => ({ ...info }), name], | |
([newInfo, newName], [oldInfo, oldName]) => { | |
console.log(newInfo, newName, oldInfo, oldName); | |
} | |
); | |
const changeData = () => { | |
info.name = "Nico"; | |
}; | |
return { | |
changeData, | |
info, | |
}; | |
}, | |
}; | |
</script> | |
<style scoped> | |
</style> |
参考结果:
# 10. 总结
- watch 侦听函数的数据源有两种类型
- 一个 getter 函数:但是该 getter 函数必须引用可响应式的对象 (比如 reactive 或者 ref)
- 直接写入一个可响应式的对象,reactive 或者 ref (比较常用的是 ref)
- watch 侦听 reactive
- watch 侦听的 reactive 获取到的 newValue 和 oldValue 的引用地址是一样的
- watch 侦听获取的 newValue 和 oldValue 本身都是 reactive 对象
- 而对 reactive 对象 使用 getter 函数,是可以获取到与 ref 一样的普通对象
- watch 侦听 ref
- watch 侦听 ref 如果直接赋值是可以侦听到的 (比如:info.value = ???)
- watch 侦听 ref 的 newValue 和 oldValue 是能获取到的 (就是引用地址不一样)
- watch 侦听的 ref,如果是一个对象,并且改变的是对象里面的属性,是侦听不到的 (比如:info.value.age++)
- watch 侦听 (reactive/ref) 对象 并传入 getter 函数, newValue 和 oldValue 可以获取到不同值
- watch 与 options API 的 watch 使用基本一致(deep/immediate)
- watch 侦听多个数据源可以使用(数组)方式