笔记整理-主要参考:vue3.chengpeiquan.com/
数据的监听
- 应用场景:监听路由变化,监听参数变化
- 特点比较:vue3保留原来的watch,新增了watchEffect进行更简单监听
watch
基础用法
// 不要忘了导入要用的 API
import { defineComponent, reactive, watch } from 'vue'
export default defineComponent({
setup() {
// 定义一个响应式数据
const userInfo = reactive({
name: 'Petter',
age: 18,
})
// 2s后改变数据
setTimeout(() => {
userInfo.name = 'Tom'
}, 2000)
/**
* 可以直接监听这个响应式对象
* callback 的参数如果不用可以不写
*/
watch(userInfo, () => {
console.log('监听整个 userInfo ', userInfo.name)
})
/**
* 也可以监听对象里面的某个值
* 此时数据源需要写成 getter 函数
*/
watch(
// 数据源,getter 形式
() => userInfo.name,
// 回调函数 callback
(newValue, oldValue) => {
console.log('只监听 name 的变化 ', userInfo.name)
console.log('打印变化前后的值', { oldValue, newValue })
}
)
},
})
批量监听
- 批量监听的应用场景:子组件有多个prop,任意一个prop变化,都需执行初始化函数重置组件的状态
-
写法1特点
- 抽离相同的处理行为为公共函数
- 定义多个监听操作,并传入公共函数
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
const message = ref<string>('')
const index = ref<number>(0)
// 2s后改变数据
setTimeout(() => {
// 来到这里才会触发 watch 的回调
message.value = 'Hello World!'
index.value++
}, 2000)
// 抽离相同的处理行为为公共函数
const handleWatch = (
newValue: string | number,
oldValue: string | number
): void => {
console.log({ newValue, oldValue })
}
// 然后定义多个监听操作,传入这个公共函数
watch(message, handleWatch)
watch(index, handleWatch)
},
})
-
写法2特点
- 数据源和回调函数都变成数组形式
- 数据源:数组形式传入,每一项都是响应式数据
- 回调参数:原来的value和newValue都变成数组,每个数组里的顺序和数据源数组排序一致
- 写法3:使用watchEffect完成批量监听
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
// 定义多个数据源
const message = ref<string>('')
const index = ref<number>(0)
// 2s后改变数据
setTimeout(() => {
message.value = 'Hello World!'
index.value++
}, 2000)
watch(
// 数据源改成了数组
[message, index],
// 回调的入参也变成了数组,每个数组里面的顺序和数据源数组排序一致
([newMessage, newIndex], [oldMessage, oldIndex]) => {
console.log('message 的变化', { newMessage, oldMessage })
console.log('index 的变化', { newIndex, oldIndex })
}
)
},
})
监听的选项
- 说明:watch API的第三个参数options,options是一个对象的形式传入
| 选项 | 类型 | 默认值 | 可选值 | 作用 | ||
|---|---|---|---|---|---|---|
| deep | boolean | false(监听reactive对象或数组时,默认是true) | true | false | 是否进行深度监听 | |
| immediate | boolean | false | ture | false | 是否立即执行监听回调 | |
| flush | string | 'pre' | 'pre' | 'post' | 'sync' | 控制监听回调的调用时机 |
| onTrack | (e)=>void | 数据源被追踪是调用 | ||||
| onTrigger | (e)=>void | 监听回调被触发时调用 |
监听选项之deep(数据变化时触发watch回调)
- 说明:若监听一个响应式的引用类型,其属性值有变化但其本身是不变的(所以不会触发watch的callback)
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
// 定义一个响应式数据,注意用的是 ref 来定义
const nums = ref<number[]>([])
// 2s后给这个数组添加项目
setTimeout(() => {
nums.value.push(1)
// 可以打印一下,确保数据确实变化了
console.log('修改后', nums.value)
}, 2000)
// 但是这个 watch 不会按预期执行
watch(
nums,
// 这里的 callback 不会被触发
() => {
console.log('触发监听', nums.value)
},
// 因为关闭了 deep
{
deep: false,
}
)
},
})
-
注意点:reactive API定义的对象无法将deep成功设置为false
- 解决方法:通过isReactive()判断,若为true则手动开启深度监听
-
// ... if (isReactive(source)) { getter = () => source deep = true // 被强制开启了 } // ...
监听选项之immediate(数据初始化时,就触发watch回调)
- 注意点:带有 immediate 选项时,不能在第一次回调时取消该数据源的监听
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
// 这一次在这里可以会触发 watch 的回调了
const message = ref<string>('')
// 2s后改变数据
setTimeout(() => {
// 这一次,这里是第二次触发 watch 的回调,不再是第一次
message.value = 'Hello World!'
}, 2000)
watch(
message,
() => {
console.log('触发监听', message.value)
},
// 设置 immediate 选项
{
immediate: true,
}
)
},
})
监听选项之flush
- 特点:flush选项用于控制调用回调时机,接受指定字符串,默认是'pre'
-
补充说明:
'pre'和'post',回调使用队列进行缓冲。回调只被添加到队列中一次- 即使观察值变化了多次,值的中间变化将被跳过,不会传递给回调,这样做不仅可以提高性能,还有助于保证数据的一致性
| 可选值 | 回调的调用时机 | 使用场景 |
|---|---|---|
| ’pre' | 将在渲染前被调用 | 允许回调在模板运行前更新 |
| ‘sync’ | 在渲染时被同步调用 | 目前没太大好处,不建议使用 |
| 'post' | 被推迟到渲染之后调用 | 若通过ref操作DOM元素与子组件,需要使用这个值来启用该选项,以达到预期的执行效果 |
停止监听(有啥应用场景?——待整理)
监听效果清理(有啥应用场景?——待整理)
watchEffect
-
特点:
- watch可访问侦听状态变化前后的值,watchEffect没有
- watchEffect会先默认执行一次,然后在属性改变时再执行
-
export default defineComponent({ setup() { const foo = ref<string>('') setTimeout(() => { foo.value = 'Hello World!' }, 2000) function bar() { console.log(foo.value) } // 使用 watch 需要先手动执行一次 bar() // 然后当 foo 有变动时,才会通过 watch 来执行 bar() watch(foo, bar) // 可以通过 watchEffect 实现 bar() + watch(foo, bar) 的效果 watchEffect(bar) }, })
-
watchEffect可用的监听选项
- watchEffect,不支持deep和immediate,其他用法一样
watchPostEffect
watchEffect API 使用 flush: 'post' 选项时的别名,具体区别详见 监听选项之 flush 部分。
Vue v3.2.0 及以上版本才支持该 API 。
watchSyncEffect
watchEffect API 使用 flush: 'sync' 选项时的别名,具体区别详见 监听选项之 flush 部分。
Vue v3.2.0 及以上版本才支持该 API