watch

监听响应式数据的变化,才能够被watch所监听到;

vue2中的watch函数

vue2中的响应式数据是写在datacomputed或者props接收的数据;才能够被watch所监听到

基本写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<h2>{{ sum }}</h2>
<button @click="sum++">点我加一</button>
</div>
</template>

<script>
export default {
data() {
return {
sum: 1,
};
},
watch: {
sum(newValue, oldValue) {
console.log(newValue, oldValue);
},
},
};
</script>

<style>
</style>

进阶写法

相比较,就是handler把原来的监听对象替换了,原来的监听对象以对象形式套在外面;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default {
data() {
return {
sum: "1",
};
},
watch: {
sum: {
immediate: true, //上来自动执行一次
deep:true, //深度监听(数据是多层级的时候用)
handler(newValue, oldValue) {
console.log(newValue, oldValue);
},
},
},
};

vue3中的watch函数

vue3中的响应式数据是被ref或者reactive包裹起来的数据;才能够被watch所监听到;

watch按需导入:import { watch } from "vue";

ref定义的一个响应式数据

ref不需要.value,因为watch监听的是一个响应式数据,如果.value了,监听的就不是proxy代理的对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ref,watch } from "vue";
export default {
setup() {
// 响应式数据
let sum = ref(1);
// watch监听 (这是一种行为,而不是数据,所以不需要返回值)
// watch() 两个参数,第一个是监听对象,对二个是回调函数
watch(sum, (newValue, oldValue) => {
console.log(newValue, oldValue);
});
return{
sum,
}
},
};

ref定义的多个响应式数据

方式一:有几个写几个watch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { ref, watch } from "vue";
export default {
setup() {
// 响应式数据
let sum = ref(1);
let msg = ref("hello");

watch(sum, (newValue, oldValue) => {
console.log(newValue, oldValue);
});

watch(msg, (newValue, oldValue) => {
console.log(newValue, oldValue);
});
return {
sum,
msg,
};
},
};

方式二:第一个参数写成数组的形式,里面放入ref响应式对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<h2>{{ sum }}</h2>
<button @click="sum++">点我加一</button>
<br />
<h2>{{ msg }}</h2>
<button @click="msg += '!'">点我加!</button>
</template>

<script>
import { ref, watch } from "vue";
export default {
setup() {
// 响应式数据
let sum = ref(1);
let msg = ref("hello");
watch([sum,msg], (newValue, oldValue) => {
console.log(newValue, oldValue);
});
return {
sum,
msg,
};
},
};
</script>

<style>
</style>

watch

ref定义的对象

watch监听ref定义的引用数据类型时,需要开启深度监听;reactive不需要开启,因为reactive源码中默认开启了深度监听;

watch监听的如果是引用数据类型,那么newVal和oldVal的值是一样的;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<template>
<div>
<input type="text" v-model="message.foo.bar.name">
<br>
<input type="text">
</div>
</template>

<script lang="ts">
import { ref, reactive, watch } from 'vue';
export default {
setup() {
// 响应式数据
const message = ref({
foo: {
bar: {
name: "fsllala"
}
}
});

// watch监听ref定义的引用数据类型时,需要开启深度监听;reactive不需要开启,因为reactive源码中默认开启了深度监听;
watch(message, (newVal, oldVal) => {
console.log(newVal, oldVal);
}, { deep: true })

return {
message,
}
}
}
</script>

监听reactive定义的一个响应式数据的全部属性

watch监听的如果是引用数据类型,那么newVal和oldVal的值是一样的;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<h2>{{ person.age }}</h2>
<button @click="person.age++">点我加一</button>
<br />
<h2>{{ person.name }}</h2>
<button @click="person.name += '!'">点我加!</button>
</template>

<script>
import { reactive, watch } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age: 18,
});

watch(person, (newValue, oldValue) => {
console.log(newValue, oldValue);
});

return {
person,
};
},
};
</script>
<style>
</style>

watch

上面问题,目前还无法解决。。。

但是其实可以将需要用到 oldValue 的 数据写成 ref的,即可解决;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ref, reactive, watch } from "vue";
export default {
setup() {
let age = ref(18);
let person = reactive({
name: "张三",
});
// 假如我需要监听 age的 oldvalue,我写成ref的形式
watch(age, (newValue, oldValue) => {
console.log(newValue, oldValue);
});

watch(person, (newValue, oldValue) => {
console.log(newValue, oldValue);
});

return {
person,
age,
};
},
};

reactive还有个问题就是:当监听全部属性的时候 强制开启了深度监听,deep:false也关不掉;会影响效率

监听reactive定义的一个响应式数据中的单一属性

第一个参数直接写 对象.属性是不行滴,因为他不是proxy代理的对象,需要写成()=>对象.属性的回调函数 形式;

值的一提的是,这样监听 reactive的单一属性,是可以检测到 oldValue的;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { ref, reactive, watch } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age:18,
});

watch(()=>person.age, (newValue, oldValue) => {
console.log(newValue, oldValue);
});

return {
person,
};
},
};

监听reactive定义的一个响应式数据中的某些属性

这个oldValue也可以监听到,只要不是监听全部的属性,都可以监听到oldValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { ref, reactive, watch } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age:18,
});

watch([()=>person.name,()=>person.age], (newValue, oldValue) => {
console.log(newValue, oldValue);
});

return {
person,
};
},
};

特殊情况

监听的是一个属性,但是这个属性又是一个对象,需要用到深度监听;因为是个对象,所以oldValue监听不到;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ref, reactive, watch } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age:18,
job:{
a:{
b:"web"
}
}
});

watch([()=>person.job], (newValue, oldValue) => {
console.log(newValue, oldValue);
},{deep:true});

return {
person,
};
},
};

vue3中的watchEffect函数

跟watch是不一样的,watchEffect是非惰性的,watchEffect一开始会自己自调用一次;

立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数;

如果用到 message 就只会监听 message ;就是用到几个监听几个 而且是非惰性 会默认调用一次;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<div>
<input type="text" v-model="message">
<br>
<input type="text" v-model="message2">
<br>
<input type="text" v-model="objMessage.name">
</div>
</template>

<script setup lang="ts">
import { ref, reactive, watchEffect } from 'vue';

let message = ref<string>("vue3.2");
let message2 = ref<string>("TS泛型");
const objMessage = reactive({
name: "fsllala",
age: 18
})

watchEffect(() => {
/**
* watchEffect 这里ref需要加.value,因为如果不加.value,监听的是一个RefImpl对象,他的引用地址是不会变的;
* 同理如果监听的是一个响应式数据,例如objMessage,即使里面的name改变了,也不会监听到,因为objMessage的引用地址是没变的;
* */
// 监听谁,写在这里面就好了,会立即执行一次;
console.log("message=====", message);
console.log("message2=====", message2.value);
console.log("objMessage=====", objMessage.name);
})
</script>

清除副作用

就是在触发监听之前会调用一个函数可以处理你的逻辑例如防抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { ref, reactive, watchEffect } from 'vue';

let message = ref<string>("vue3.2");
let message2 = ref<string>("TS泛型");
const objMessage = reactive({
name: "fsllala",
age: 18
})

watchEffect((fsl) => {
// 数据改变的时候,会先打印 before~
console.log("message=====", message.value);
console.log("message2=====", message2.value);
console.log("objMessage=====", objMessage.name);
fsl(() => {
console.log("before~");
})
})

停止监听

停止跟踪 :watchEffect 返回一个函数 ,调用之后将停止更新;

离开界面可以做停止监听;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<input type="text" v-model="message">
<button @click="stopWatch">stopWatch</button>
</div>
</template>

<script setup lang="ts">
import { ref, reactive, watchEffect } from 'vue';

let message = ref<string>("vue3.2");

// watchEffect返回一个停止监听的函数,调用了就不会监听了;
const stop = watchEffect((fsl) => {
console.log("message=====", message.value);
})

const stopWatch=()=>stop();

</script>

更多的配置项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<input type="text" v-model="message" id="inputId">
</div>
</template>

<script setup lang="ts">
import { ref, reactive, watchEffect } from 'vue';

let message = ref<string>("vue3.2");

watchEffect(() => {
// watchEffect 不加 { flush: "post" } ,domID获取不到,因为setup中dom还没渲染,所以为null;
let domID: HTMLInputElement = document.getElementById('inputId') as HTMLInputElement;

console.log("message=====", message.value);
console.log("inputId=====", domID);

}, { flush: "post" })

</script>

断言 (assertion) 是一种在程序中的一阶逻辑 (如:一个结果为真或假的逻辑判断式),目的为了表示与验证软件开发者预期的结果 —— 当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。

pre sync post
更新时机 组件更新前执行 强制效果始终同步触发 组件更新后执行