核心功能1 :监听 ref 基础类型
- **watch 作用**:用来**监视响应式数据的变化**,数据变化后执行回调函数。
**监听 ref 基础类型**
watch(sum, (newVal, oldVal)=>{})
-
直接传入 `ref` 对象本身(不用 `.value`)
-
回调参数:`newValue` 新值、`oldValue` 旧值
**watch 返回值:停止监听函数**
-
`const stopWatch = watch(...)`
-
调用 `stopWatch()` 可以**手动取消监视**,后续数据再变化,回调不再执行
-
本例逻辑:sum ≥5 时,调用停止函数,关闭监听
核心:watch 监听 ref 基本类型直接传 ref 对象;它返回一个停止函数,可以手动终止侦听。
javascript
<template>
<div class="person">
<h2>Watch监视Ref定义的基本类型的值</h2>
<h2>当前求和:{{ sum }}</h2>
<button @click="changeSum">点我+1</button>
</div>
</template>
<script lang="ts">
//导出组件名称,叫做"App"
export default {
name: "Person"
}
</script>
<script lang="ts" setup>
import {ref, watch} from "vue"
let sum = ref(0)
function changeSum() {
sum.value += 1
}
const stopWatch = watch(sum, (newValue, oldValue) => {
console.log(newValue, oldValue)
if (sum.value >= 5) {
console.log("RefImpl sum.value值已经超过5,不再监视")
//与watch返回值一致
stopWatch()
}
})
</script>
<style scoped>
.person {
background-color: green;
}
</style>

核心功能2 :监听 ref 对象类型
1.ref可以包裹对象,person是RefImpl,真实对象在person.value里
2.监听ref对象,deep默认false:只监听整个person.value被替换,修改内部属性不触发
整个对象替换案例:person.value = {name: "李四", age: 20}
3.deep设为true:深度监听,替换对象、修改内部属性都能触发
4.deep设为true,只改对象内部属性时,newValue和oldValue是同一个对象,看不出新旧区别
5.immediate默认false,初始化不执行;immediate:true初始化立刻执行回调,newValue是初始值,oldValue是undefined
6.watch可以接收返回的停止函数,调用就能取消监听
7.监听ref基础类型不用deep,监听ref里面对象的属性变化需要开启deep:true
javascript
<template>
<div class="person">
<h2>Watch监视Ref定义的基本类型的值</h2>
<h2>当前求和:{{ sum }}</h2>
<button @click="changeSum">点我+1</button>
<hr>
<h2>Watch监视Ref定义的对象类型的值</h2>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="changPerson">改变人</button>
</div>
</template>
<script lang="ts">
//导出组件名称,叫做"App"
export default {
name: "Person"
}
</script>
<script lang="ts" setup>
import {ref, watch} from "vue"
let sum = ref(0)
//ref引用对象类型,person是RefImpl,提取值需要.value
let person = ref({name: "张三", age: 18})
function changPerson() {
person.value = {name: "李四", age: 20}
}
function changeSum() {
sum.value += 1
}
const stopWatch = watch(sum, (newValue, oldValue) => {
console.log(newValue, oldValue)
if (sum.value >= 5) {
console.log("RefImpl sum.value值已经超过5,不再监视")
//与watch返回值一致
stopWatch()
}
})
/**
* 第一个参数监视的对象
* 第二个参数,回调函数,参数1新值,参数2旧值
* 如果是对象的属性改变,新值与旧值相同
* 如果是对象的属性改变,新值与旧值相同
* 如果是对象的改变,新值是新对象,旧值是旧对象
*/
watch(person, (newValue, oldValue) => {
console.log(newValue, oldValue) //deep=true {name:"张三",age:18} undefined
}, {
deep: false, //true监控值属性变化
immediate: false//true初始化时就执行一次
})
</script>
<style scoped>
.person {
background-color: green;
}
</style>

核心功能3:监听reactive对象类型
- reactive创建的对象,watch直接监听它,默认自动开启深度监视,并且这个deep无法关闭
- 不能直接写person = {name:"李四",age:20},这样会切断响应式
- 如果想要整体更新reactive对象里面的数据,可以用Object.assign(person,新对象),
- 回调函数中回调里 newValue 和 oldValue 是同一个对象
- 只修改reactive内部属性时,也会触发,但回调里newValue和oldValue是同一个对象发
- immediate默认值是false 案例: watch(person, (newValue, oldValue) => { console.log(newValue, oldValue) })
- style加scoped,样式只在当前组件生效
javascript
<template>
<div class="person">
<h2>Watch监视Reactive定义的对象类型的值</h2>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="changPerson">改变人</button>
</div>
</template>
<script lang="ts">
//导出组件名称,叫做"Person"
export default {
name: "Person"
}
</script>
<script lang="ts" setup>
import {ref, watch,reactive} from "vue"
let person = reactive({name: "张三", age: 18})
function changPerson() {
// person = {name: "李四", age: 20} 这是无法替换的
Object.assign(person,{name:"李四",age:20})
}
/**
* 第一个参数监视的对象
* 第二个参数,回调函数,参数1与参数2在 监视Reactive对象时时同一个对象
* 默认开启深度监视且无法关闭
*/
watch(person, (newValue, oldValue) => {
//都是同一个对象,因为监视的person是同一个,newValue与oldValue是同一个地址
console.log(newValue, oldValue)
})
</script>
<style scoped>
.person {
background-color: green;
}
</style>

核心功能4:监听ref或reactive对象中的属性
-
reactive对象内部属性监听,推荐使用函数返回式作为watch第一个参数 案例:watch(()=>person.name,(newVal,oldVal)=>{console.log(newVal,oldVal)})
-
修改reactive对象不能直接person=新对象,会切断响应式,直接修改属性是可行的
-
监听reactive里嵌套对象(person.car),即使写成函数形式,想要监听内部属性修改,需要配置deep:true
-
只修改嵌套对象内部属性时,回调的newValue和oldValue指向同一个引用
-
监视ref或reactive定义的【对象类型】数据中的某个属性,注意点如下: 1.若该属性值是【基本类型】,需要写成函数形式。 2.若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。注意
person.car = {c1: "雅迪", c2: "艾玛"} 是替换属性,不是整个person的Object.assign -
immediate默认值为false
-
style加scoped,样式仅当前组件生效
监听reactive的对象,与监听reactive中对象的对象,有什么区别?
1、直接监听整个reactive对象 watch(person, callback) 自动自带deep深度监听,deep关不掉;person里面不管多少层属性改动都能触发回调。
2、监听reactive里面嵌套的对象 watch(()=>person.car, callback) 不会自动开启deep,想要监听car内部c1、c2这类属性变化,必须手动加deep:true;如果不加deep,只有给car整体替换新对象的时候才触发。
补充: 两种方式,单纯修改内部属性时,newValue和oldValue都是同一个引用。 统一推荐都用函数写法作为watch第一个参数。
javascript
<template>
<div class="person">
<h2>Watch监视Reactive定义的对象中的属性</h2>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>第一辆车:{{ person.car.c1 }}</h2>
<h2>第二辆车:{{ person.car.c2 }}</h2>
<button @click="changName">改变名字</button>
<button @click="changC1">改变第一辆车</button>
<button @click="changCar">改变整车</button>
</div>
</template>
<script lang="ts">
//导出组件名称,叫做"Person"
export default {
name: "Person"
}
</script>
<script lang="ts" setup>
import {watch, reactive} from "vue"
let person = reactive({name: "张三", age: 18, car: {c1: "奔驰", c2: "宝马"}})
function changName() {
// person = {name: "李四", age: 20} 这是无法替换的且切断了响应式
person.name = "李四"
}
function changC1() {
person.car.c1 = "奥迪"
}
function changCar() {
person.car = {c1: "雅迪", c2: "艾玛"}
}
/**
* 监听对象属性中的基本类型
* 第一个参数监视的对象属性,建议使用函数
* 第二个参数,回调函数,参数1新值,参数2旧值
*/
watch(() => {
return person.name
},
(newValue, oldValue) => {
console.log(newValue, oldValue)
})
//监听对象中的对象类型
//如果第一个参数直接写的是person.car,那么只能监听到原来的car变化,整个car替换监听不到
//所以第一个参数官方推荐使用函数返回,才能监听的到变化
watch(() => {
return person.car
},
(newValue, oldValue) => {
//实际上newValue与oldValue是一个,因为是同一个引用地址
console.log(newValue, oldValue)
},
{"deep": true})//监听属性变化
</script>
<style scoped>
.person {
background-color: green;
}
</style>

核心功能5:监听多个KEY
- watch可以监听多个响应式数据,第一个参数传数组,案例:watch(temp, height,(newVal)=>{}),newVal是新值组成的数组,可以解构拿到每一项新值
- watchEffect不用明确指定要监听谁,回调内部用到哪些响应式数据,就自动追踪依赖这些数据
- watchEffect默认初始化就会执行一次回调,自动收集依赖,后续依赖的数据变化就重新运行回调
- watch需要手动指定监听源;watchEffect自动收集依赖
- watchEffect没有新旧值参数,拿不到oldValue
- watchEffect也会返回停止函数,调用可以停止侦听 案例:const stop = watchEffect(()=>{}) stop()
- style加scoped,样式只作用当前组件
javascript
<template>
<div class="person">
<h2>Watch数组+WatchEffect</h2>
<h2>水温:{{ temp }}°C</h2>
<h2>水位:{{ height }}CM</h2>
<button @click="addTemp">增加水温</button>
<button @click="addHeight">增加水位</button>
<button @click="changeAge">年龄</button>
<button @click="changeName">姓名</button>
</div>
</template>
<script lang="ts">
//导出组件名称,叫做"Person"
export default {
name: "Person"
}
</script>
<script lang="ts" setup>
import {reactive, ref, watch, watchEffect} from "vue"
let temp = ref(10);
let height = ref(0);
let person = reactive({name: "张三", age: 18})
function addTemp() {
temp.value += 10
}
function addHeight() {
height.value += 10
}
function changeAge() {
person.age = 19
}
function changeName(){
person.name = "李四"
}
watch([temp, height, person, ()=>{return person.name}], (newValue,oldValue) => {
console.log(newValue) //[10, 0, Proxy(Object), '张三']
let [t, h] = newValue
if (t >= 60 || h >= 80 || person.age == 19) {
alert("发送报警")
}
})
watchEffect(() => {
if (temp.value >= 60 || height.value >= 80) {
alert("发送报警")
}
})
</script>
<style scoped>
.person {
background-color: green;
}
</style>
