文章目录
- watch
- 1 watch的用法
- 2 watch的高级用法
- 3 watch性能优化
- watchEffect
- 1 停止监听
- 2 侦听多个状态
- 3 懒执行
- watch和watchEffect的区别
- watch和computed的区别
watch
watch特性进行了一些改变和优化。与computed不同,watch通常用于监听数据的变化,并执行一些副作用,侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数。
1 watch的用法
- watch的基本用法
watch(source, callback, options?)
source表示要监听的数据,可以是一个响应式的数据对象、一个计算属性或一个方法的返回值;callback表示当数据发生变化时要执行的回调函数;options表示watch的一些配置选项,例如immediate、deep、flush等。
- 监听 ref 定义的响应式数据
<template> <div> <div>值:{{count}}</div> <button @click="add">改变值</button> </div> </template> <script> import { ref, watch } from 'vue' export default { setup () { const count = ref(0) const add = () => { count.value++ } watch(count, (newVal, oldVal) => { console.log('值改变了', newVal, oldVal) }) return { count, add } } } </script>
- 监听 reactive 定义的响应式数据
<template> <div> <div>姓名:{{ obj.name }}</div> <div>年龄:{{ obj.age }}</div> <button @click="changeName">改变值</button> </div> </template> <script> import { reactive, watch } from 'vue' export default { setup () { const obj = reactive({ name: 'zs', age: 14 }) const changeName = () => { obj.name = 'ls' } watch(obj, (newVal, oldVal) => { console.log('值改变了', newVal, oldVal) }) return { obj, changeName } } } </script>
注:此处监听的新值和旧值相同,主要是因为新旧值引用地址是相同的,
此处可采取computed计算属性先实现深拷贝。
<template>
<div>
<div>姓名:{{ obj.name }}</div>
<div>年龄:{{ obj.age }}</div>
<button @click="changeName">改变值</button>
</div>
</template>
<script>
import { reactive, watch, computed } from 'vue'
export default {
setup () {
const obj = reactive({
name: 'zs',
age: 14
})
const changeName = () => {
obj.name = 'ls'
}
const deepObj = computed(() => {
return JSON.parse(JSON.stringify(obj))
})
watch(deepObj, (newVal, oldVal) => {
console.log('值改变了', newVal, oldVal)
})
return {
obj,
changeName
}
}
}
</script>
- 监听多个ref的值,采用数组形式
<template> <div> <!-- 侦听多个变量 --> 姓名:<input v-model="userName" type="text"> 年龄:<input v-model="age" type="number"> </div> </template> <script lang="ts"> import { defineComponent, ref, watch } from 'vue' export default defineComponent({ setup () { const userName = ref<string>('张三') const age = ref<number>(18) watch([userName, age], (newVal, oldVal) => { console.log(newVal, oldVal) }) return { userName, age } } }) </script>
- 监听reactive对象中某个属性的变化
<template> <div> <div>{{obj.name}}</div> <div>{{obj.age}}</div> <button @click="changeName">改变值</button> </div> </template> <script> import { reactive, watch } from 'vue' export default { setup () { const obj = reactive({ name: 'zs', age: 14 }) const changeName = () => { obj.age++ } watch(() => obj.age, (newVal, oldVal) => { console.log(newVal, oldVal) }) return { obj, changeName } } } </script>
2 watch的高级用法
- 一次性监听
每当被侦听源发生变化时,侦听器的回调就会执行。如果希望回调只在源变化时触发一次,请使用 once: true 选项。<template> <div> <div>{{obj.name}}</div> <div>{{obj.classInfo.studentNum}}</div> <button @click="changeName">改变值</button> </div> </template> <script> import { reactive, watch } from 'vue' export default { setup () { const obj = reactive({ name: 'zs', age: 14, classInfo: { className: '一年级', studentNum: 45 } }) const changeName = () => { obj.classInfo.studentNum++ } watch(() => obj, (newVal, oldVal) => { console.log(newVal, oldVal) }, { once: true }) return { obj, changeName } } } </script>
- 深层侦听器
直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:deep 选项,强制转成深层侦听器:<template> <div> <div>{{obj.name}}</div> <div>{{obj.classInfo.studentNum}}</div> <button @click="changeName">改变值</button> </div> </template> <script> import { reactive, watch } from 'vue' export default { setup () { const obj = reactive({ name: 'zs', age: 14, classInfo: { className: '一年级', studentNum: 45 } }) const changeName = () => { obj.classInfo.studentNum++ } watch(() => obj, (newVal, oldVal) => { console.log(newVal, oldVal) }, { deep: true // 不添加deep属性,watch不会触发 }) return { obj, changeName } } } </script>
注意:当我们使用deep选项时,watch的性能会受到一定的影响,因为Vue需要对对象或数组进行递归遍历。因此,只有在必要的情况下才应该使用deep选项
- 即时回调的侦听器
watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。通过传入 immediate: true 选项来强制侦听器的回调立即执行:import { reactive, watch } from 'vue' const state = reactive({ count: 2 }) watch( () => state.count, (newVal, oldVal) => { console.log(newVal, oldVal) }, { immediate: true } )
- flush 回调的触发时机
- sync:同步模式下执行
- pre:在数据变化之前执行回调函数
- post:在数据变化之后执行回调函数,但是需要等待所有依赖项都更新后才执行
import { reactive, watch } from 'vue' const state = reactive({ count: 2 }) watch( () => state.count, (newVal, oldVal) => { console.log(newVal, oldVal) }, { flush: sync } )
3 watch性能优化
- 使用computed代替watch
import { reactive, computed } from 'vue' const state = reactive({ count: 2 }) const doubleCount = computed(() => { return state.count * 2 }) console.log(doubleCount.value) // 输出:2 state.count++ console.log(doubleCount.value) // 输出:4
- 使用throttle和debounce控制回调函数的执行频率
频繁地监听一个值的变化,并在变化时执行一些操作。如果回调函数执行的太频繁,会影响性能。为了避免这种情况,我们可以使用throttle和debounce控制回调函数的执行频率
throttle可以用于控制函数在一定时间内只能执行一次,
debounce可以用于控制函数在一定时间内不会连续执行import { reactive, watch } from 'vue' import { throttle } from 'lodash-es' const state = reactive({ count: 3 }) watch( () => state.count, throttle((newVal, oldVal) => { console.log(newVal, oldVal) }, 1000) ) state.count++
watchEffect
watchEffect 函数来创建高级侦听器。与 watch 和 computed 不同,
watchEffect 不需要指定依赖项,自动追踪响应式状态的变化,并在变化时重新运行
<template>
<div>
<div>{{obj.name}}</div>
<div>{{obj.age}}</div>
<button @click="changeAge">改变值</button>
</div>
</template>
<script>
import { reactive, watch, watchEffect } from 'vue'
export default {
setup () {
const obj = reactive({
name: 'zs',
age: 14,
})
const changeName = () => {
obj.age++
}
watchEffect(() => {
console.log(obj.age)
})
return {
obj,
changeAge
}
}
}
</script>
1 停止监听
watchEffect 函数不会返回一个停止侦听的函数。如果我们需要停止侦听,我们可以将 watchEffect 的返回值设为 null
const stop = watchEffect(() => {})
// 当不再需要此侦听器时:
stop()
2 侦听多个状态
如果需要侦听多个响应式状态,可以在 watchEffect 函数中使用这些状态,并在函数中返回一个计算值,
<script>
import { reactive, watchEffect } from 'vue'
export default {
setup () {
const state = reactive({
count1: 2,
count2: 3
})
watchEffect(() => {
const sum = state.count1 + state.count2
console.log(sum)
})
// 改变状态,输出 5
state.count1++
// 改变状态,输出 8
state.count2 += 2
return {
}
}
}
</script>
3 懒执行
watchEffect 函数也支持懒执行(lazy evaluation)。如果我们将 watchEffect 的第二个参数设置为 { lazy: true },则这个函数会在第一次访问响应式状态时才会被运行
const state = reactive({
count: 2
})
watchEffect(() => {
console.log(state.count)
}, { lazy: true })
// 改变状态,输出 2 3
state.count++
watch和watchEffect的区别
watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
watch 监听函数可以添加配置项,也可以配置为空,配置项为空的情况下,
watch的特点为:
- 有惰性:运行的时候,不会立即执行;
- 更加具体:需要添加监听的属性;
- 可访问属性之前的值:回调函数内会返回最新值和修改之前的值;
- 可配置:配置项可补充 watch 特点上的不足:
- immediate:配置 watch 属性是否立即执行,值为 true 时,一旦运行就会立即执行,值为 false时,保持惰性。
- deep:配置 watch 是否深度监听,值为 true 时,可以监听对象所有属性,值为 false 时保持更加具体特性,必须指定到具体的属性上。
watchEffect 特点 - 非惰性:一旦运行就会立即执行;
- 更加抽象:使用时不需要具体指定监听的谁,回调函数内直接使用就可以;
- 不可访问之前的值:只能访问当前最新的值,访问不到修改之前的值;
watch和computed的区别
computed(计算属性):
- 定义:computed是一个基于其依赖的数据进行计算的值,可以缓存计算的结果。
- 工作原理:Vue会自动追踪computed属性方法对data的数据依赖,并在依赖数据发生变化时重新计算computed属性的值。
computed特点:- 支持缓存,只有依赖数据发生改变时才会重新进行计算。
- 不支持异步操作,如果computed内有异步操作,则无效。
- 必须通过return返回一个值。
- 适合当一个属性受多个属性影响时使用。
- 在模板中可以直接使用,访问时不需要加括号。
watch(监听):
- 定义:watch用于监听响应式数据的变化,并在数据发生变化时执行一些自定义的操作。
- 工作原理:当监听的数据发生变化时,会直接触发相应的操作。
watch特点:- 不支持缓存功能,监听的数据发生变化时会直接触发相应的操作。
- 支持异步操作,适合监听路由和设置计时器等。
- 可以没有返回值。
- 适合当一条数据影响多条数据时使用。
- 在模板中不能直接使用,需要通过事件或方法进行调用。