>[success] # watch
1. 侦听指定的一个或多个响应式数据, 一旦数据变化, 就自动执行回调函数,
~~~
// 侦听单个来源
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle
// 侦听多个来源
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle
type WatchCallback<T> = (
value: T,
oldValue: T,
onCleanup: (cleanupFn: () => void) => void
) => void
type WatchSource<T> =
| Ref<T> // ref
| (() => T) // getter
| T extends object
? T
: never // 响应式对象
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 默认:false
deep?: boolean // 默认:false
flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
~~~
2. **默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数**,可以通过配置`immediate`为`true`, 来指定初始时立即**执行第一次**
3. 通过配置`deep`为`true` 后**如果源是对象,强制深度遍历,以便在深层级变更时触发回调**,在大型结构时候会比较耗费性能
4. 在3.x 中 `watch `还增加了`flush` `onTrack` `onTrigger`,三个配置
* `flush : 'pre' | 'post' | 'sync' `默认是`pre`,`pre` 会在 Vue 组件更新**之前**被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态,`post`,在侦听器回调中能访问被 Vue 更新**之后**的 DOM,`sync` 是每一步都会触发(实际vue 为了防止使用队列缓冲回调。回调只会添加到队列中一次,即使所监视的值更改了多次。中间值将被跳过,并且不会传递给回调,但`sync`是次次触发) ,具体可[查看的案例参考](https://github.com/vuejs/core/issues/5721)
* `onTrack` `onTrigger` -- 备注 用来做调试用的
>[danger] ##### 使用参数情况
1. 直接写入一个可响应式的对象,`reactive`或者`ref`,但`reactive` 会让侦听器自动启用深层模式
* [ref ](https://sfc.vuejs.org/#eNp9UUtOwzAQvcrIm7ZSGguWVVrBFViw8iZ1J21KYlu20y6iSNwAugT2sEHcqnxuwTiOECoSC/9m5r15b9yyS2PSXYNsxjInbWk8OPSNWQgFUNZGWw8t7HMvN4nFAjoorK5hRJBRKJFaOQ+qqa8oOQcqGZ9NQoJWOIpGSV9qBXKTqzWOJ22IwoBId3nVIOHOQ7QbcMA5fDwdjvevny/Px7tDCPUKxhGVjBXur/Mq0dWKjsl8MZAGMbrCtNLrk5KenvaMR5Nkjx4ea1PlHnuzWfQMbRu7dL2cjMdwtmy8JxsXsirlzVyw6EewxfvD29ftY9Sb8VjWQ0K3nw4sYXGa0zo36dZpRRPvZYsh4QSbwWBEMJpveAu28d64GeeukOGfti7Vds3pltpG+bLGFF09XVq9d2iJWLDkFwen4A7t1KJaoUX7H+dJ6R/e4Ys61n0DoibImg==)为基本类型情况,直接监听直接触发
~~~html
<script setup>
import { watch,ref } from 'vue'
const numRef = ref(1)
function change(){
numRef.value = 2
}
// 监听触发
watch(numRef,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
</script>
<template>
<p>
{{numRef}}
</p>
<button @click="change">普通监听</button>
</template>
~~~
* [reactive](https://sfc.vuejs.org/#eNp9Us1O3DAQfhVrLmSlbHyPsqtW6hP00EvdQzbMbkIT27KdjdooUi89A0fgDFwQEieeaPl5C8ZJWGBBRIoTe+b75pvP08JXraN1jRBDYjNTaMcsulrPhWSsqLQyjrWsSV2WhwbTzBVrZB1bGlWxPcLt+Tz/skxJ61ghl+r7c96MPUOCVqYVxgKaphEQ5mqxKNDGPwX8pa0AJ+AX6yZbsmUtCacky/JUrvAbog4mbR96UyIamSJd2zwgNiLqWTq/7LB8zOCFkVIBf+gRsAVzzu7PjjdHVw+XF5vDY3/U2xC8RoeBxOZHWoaq3KfPZDYfS3g3VIlRqVY7KX0BWhM++E1O08ZhpcvUYe97MtjP2vZ1ra6XlfAhmCxq56i1L1lZZL9nAoYeBczvTq4f/50O2hM+pH0G8eZ62O3N5v/5O5gXuhUHIQwzMa1SHR1YJWlu+o7FGLACYjZ6IIAGxO8F5M5pG3Nul5mftgMbKbPi9BeZWrqiwghtNV0Y1Vg0ROyH4oWD0+EazdSg3EeD5jPOndR3vOP9dtA9AfcRE7A=) 无论触发深层和浅层都会触发监听器回调函数,因为`reactive `会默认开启深层监听
~~~html
<script setup>
import { watch,reactive } from 'vue'
const infoReactive = reactive({name:"www",hobbies:["z","t"] })
function changeDeep(){
infoReactive.hobbies.push("zt")
}
function change(){
infoReactive.name = "yyyy"
}
// 监听触发
watch(infoReactive,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
</script>
<template>
<p>
{{infoReactive}}
</p>
<button @click="change">普通监听</button>
<button @click="changeDeep">深入监听</button>
</template>
~~~
* [ref ](https://sfc.vuejs.org/#eNqlk7+O1DAQxl/FcnNZKWv3UXYFEk9AQYMpstnJJkdiW7azEUSRaCgRXMlRAw1CooKCx7mFu7e4cRzt/T/pdJGixPbM7xuPP/f0qdZs2wJNaGpzU2lHLLhWL4UkpGq0Mo70pMtcXsYGCjKQwqiGHGDKgQ/JlbSOVLJQz3F1QTAm6mXWQCJo13WCxqVarSqwyUtB3+JQUCfoKzLMfLZ/i1bmrlKS5GUmN/AMQEez3q9gAYHLtlndAptITLe2jJCGoJEy3IK5A+ErwyoFfYOPoPts/3JO/n852n36cfr92+7jkZ8a9x1dQcSRhO5FVseqXuNntlhOQr4TqgZWq821kFFlv1+UOfnz4eTv8R0yDxfAnl4hTl25rPjv96+wtxH1SL2Aj/s1HlXiTAteKuXBPmgcHDhodJ05GG2UBjeRvp8kh7HnKQ/z6ap1Dg/uSV5X+euFoBdGEHSJle/efw3FpzyE3pfmUz7/PHt3fEuKr3JfGY1p8Pe8yTQ7tEriHRi3LqYFK2hCpmYIio73Y0FL57RNOLdF7m/OoWXKbDj+MdNKVzXAwDbzlVGdBYNgb/oLBsfJLZi5AbkGA+Y+5rXQG9zJvgMdzgFlTGEt)监听是一个对象,此时需要监听到`.value`才可,由于`ref`绑定的是对象他将会调用`reactive` 因此也是深层监听,但如果 仅仅想监听`ref `并且要触发可以使用`deep:true` 开启监听
~~~html
<script setup>
import { watch,ref } from 'vue'
const infoRef = ref({name:"www",hobbies:["z","t"] })
function changeDeep(){
infoRef.value.hobbies.push("zt")
}
function change(){
infoRef.value.name = "yyyy"
}
// 监听触发
watch(infoRef.value,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
// 不会触发
watch(infoRef,(newVal,oldVal)=>{
console.log(newVal,oldVal,"不会触发")
})
// 深监听
watch(infoRef,(newVal,oldVal)=>{
console.log(newVal,oldVal)
},{deep:true})
</script>
<template>
<p>
{{infoRef}}
</p>
<button @click="changeDeep">深入监听</button>
<button @click="change">普通监听</button>
</template>
~~~
2. 一个`getter`函数:但是该`getter`函数必须引用可响应式的对象(比如`reactive`或者`ref`),和计算属性类似在函数中的值有发生变化的都会进行监听,因此监听的其实是`watch`第一个参数的`getter `函数
* [ref](https://sfc.vuejs.org/#eNqtUr1O3EAQfpXRNvgUn1dJefKdkldIkcqN8c3dmdi7q931XWFZQkqaFCigKEpCKhpogIMOJHgbfBxvwa7X/AsaKKz1zs433zczX0k+CRFMCyQ9EqpEpkKDQl2IQcQA0lxwqaGEWayTiS9xBBWMJM9hxUBWbErCmdLAivyzeeyDSfHed+yD+ewxKliiU84gmcRsjF6ntFFoEcE0zgqEd334YMNVCwRK4eJkY/ntfLm3W//caiCNBs/r9AcO63sMZ1/izOfZ0Bwm3pa2kniGQcbHj1Iajht1lqQ+W68350NEcfl/q97cv6N7AzZLsPjxq944utz+vvg7r+eny+OdRoNfWs6elgU+0ONUgJvK82Lc2F4lyeiBxe+j+uDPzVBC6vZvNm8uGnORxRobH4TODlCWjr9qFhVSFw5XC63Nhj8mWZp87UfErToig8W/w6v1bddUSF1aA7FstwzEJ85o3TwWwZrizJixaSVqH1REetA2FxFjPXuPyERroXqUqlFiLbymAi7H1PwFsmA6zTFAlXdXJZ8plKZwRPx7NagJTlF2JbIhSpQv1XyU+qRua96KVNcA8zs0) 基本类型时候,使用函数的形式发现,监听时候如果是直接是 `ref` 对象可以利用`深监听`或者是监听`.value `才可,但监听的值完全其实是 函数返回值的前后变化,深监听的时候函数返回的是`ref `对象因此回调是 `ref `对现象,但`.value` 时候监听的是固定值因此返回是值
~~~html
<script setup>
import { watch,ref } from 'vue'
const numRef = ref(1)
function change(){
numRef.value += 2
}
// 不能触发
watch(()=>numRef,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
// 开启deep监听触发
watch(()=>numRef,(newVal,oldVal)=>{
console.log(newVal,oldVal) // 打印的是对象
},{deep:true})
// 监听 value 触发
watch(()=>numRef.value,(newVal,oldVal)=>{
console.log(newVal,oldVal) // 打印是 数字
})
</script>
<template>
<p>
{{numRef}}
</p>
<button @click="change">普通监听</button>
</template>
~~~
* 监听[reactive](https://sfc.vuejs.org/#eNqtVd1OE0EUfpXJ3LQk292oXDWFaOITeOGN68V2mbaL3Z/MztLopgkJAhqBQkBFSlATfwhBKMZorTS8zM62XvkKntndlhYLJg1Nurszc84333zn7Lc+vuM48pxHcBbnXJ0aDkMuYZ4zrVoIGaZjU4Z8VNGYXpIo0XRmzBFURQVqmygFeSkRJ/5Ity2XIcMq2Pd6cVOol5L2Lc0kWRVXKhUVSyU7nzeIm32g4icwVDFT8UNUneiDFTwL8mwL6SXNKpK7hDjpCT9aGtpCTpBkx3NLaUADoAilKi4XUEYjCGLAVMWP4afifrKioKC52qlv8PXD7udPvLbB25tB+6yztY+KhDFCEV9uhy8bQXs3aLbC1nr4+vuf0xVe3+s2FnjtgO/uh18+xDGdnafdsy1Y4vOnvLbNV15BcPh8NTjdibEhsbPWgBS+tMiPfiYEIDRoLQXtpXBlma9vhF/fw4yg1c8TgVF10umJqenBg0lpi1Tua2XJLs/ADVaT04tC2WUil+3icIiUupGKxesXQojQ24rX38E5YRhrwp81wuNa0JwPmgf9U10vnZsJHcmfgQbIMuqRYWoDQgwpdDmNXsOMQ+fWVerEnQHVGuoNBLWDVhBRUflj6aAbQLFov0tYiqYci2IGSih4iWZG8LIhmPlfSUfpg8Lt46S2m6u8tdU5+cX3XgxQvi5RJ0eJOlBXeO4utDv1b+Hax1i9hNbbVrh7eE5rBKtxRZwUIvZI5ZTYFsEQYcCI6ZQ1RiJ7zMUuiXx/cNtq5B45JV7M5T3GwIFu62VDfzSl4tiKVDwdvjn6PZ+8SzklDrsqRXigSPtxwhcTJQbSBNE+OSzh2LozpubIs65tgb1H51aTBVfFWZQooWLwcTFWcYkxx80qilvQxUdh1pVtWlTgSaaexQyTyMQ1M3lqV1xCAVh49zmGApNzhGYosWYIJfQqzAuh/+AmNlzF1b9gosaY),分几种情况首先**当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发**,也就是说下面的 **"1"** 这种情况就不会触发因为返回的getter 函数中的内容虽然变了但指向没变因此不会触发,**"2"** 这种情况监听了的对象虽然指向没变但是我们监听了他所有内部属性如果变化就会监听到,**"3"** 不会触发原理和指向问题是一样的指向没变**"3-1"** 因为是基本类型因此 值改变了监听就变了,**"4"** 直接监听的是一个响应式对象即触发到监听的,**"4-1"** 基本类型是不会触发必须是响应式对象
~~~html
<script setup>
import { watch,reactive } from 'vue'
const infoReactive = reactive({name:"www",hobbies:["z","t"] })
function changeDeep(){
infoReactive.hobbies.push("zt")
}
function change(){
infoReactive.name = "yyyy"
}
// 不监听触发当使用 getter 函数作为源时,回调只在此函数的返回值变化时才会触发,现在内容
// 变了但指向没变 不会触发
watch(()=>infoReactive,(newVal,oldVal)=>{
console.log(newVal,oldVal,'1')
})
// 会触发因为会监听到每一个值变化
watch(()=>infoReactive,(newVal,oldVal)=>{
console.log(newVal,oldVal,'2')
},{deep:true})
// 不会触发指向没变
watch(()=>infoReactive.hobbies,(newVal,oldVal)=>{
console.log(newVal,oldVal,'3')
})
// 会触发使用了getter 函数 但只会返回监听的值
watch(()=>infoReactive.name,(newVal,oldVal)=>{
console.log(newVal,oldVal,'3-1') // yyyy www 3-1
})
// 会触发因为infoReactive.hobbies 是一个响应类型
watch(infoReactive.hobbies,(newVal,oldVal)=>{
console.log(newVal,oldVal,'4')
})
// 不会触发不能直接监听一个基本类型
watch(infoReactive.name,(newVal,oldVal)=>{
console.log(newVal,oldVal,'4-1')
})
</script>
<template>
<p>
{{infoReactive}}
</p>
<button @click="change">普通监听</button>
<button @click="changeDeep">深入监听</button>
</template>
~~~
* `ref`绑定的是对象他的`.value`将会调用`reactive`,因此现象可以参考 `reactive `场景
* **注重说明函数监听是其[返回值变化](https://sfc.vuejs.org/#eNp9UUFOhDAUvUrTzUBkILNxMWEmegUXrrrpdD4MI7RNWyCGkHgEXapXMN5KjbfwlxIzaiIJ0Pa9vv/++wO91DrtWqBrmlthKu2IBdfqLZOEVI1WxpGB9NyJQ2KgICMpjGrIAq8sPMW/RChpHZG8AbJBUsRo3/eMxicgLxEjHlyd47lHilYKVylJxIHLEqJ4mPiTTtrxuvU3GL3Fh1EPjXjPTVaiKN5soxPimS8Q1nESSeiveZ2oeo8/ZM7C3omqIa1V+ZOSLFaLyeyI3zwLOWACuHHQ6Jo7mPLIQyxkGCpZqCvg6L+DEX0hlgUw37XOYVMXoq7EzYbR0B2j2/fH18+7p4/nh7f7lzwLtFAk+65CExpCXzZcp0erJA5mss9mwDK6JnNDjOIY/J7Rg3ParrPMFsKP82hTZcoMV6lppaswKLDNcmdUb8GgMKPJiUaGhx2YpQG5BwPmP81f1D+686RGOn4Bt6fRvA==)**
![](https://img.kancloud.cn/a3/3d/a33dd52347a78ec399636bcc7cd1f8cd_202x31.png)
~~~
<script setup>
import { watch,ref } from 'vue'
const name =ref("www")
const age = ref(16)
function change(){
name.value = "yyyy"
}
watch(()=>(name.value + age.value),(newVal,oldVal)=>{
console.log(newVal,oldVal,'1')
})
</script>
<template>
<p>
{{infoReactive}}
</p>
<button @click="change">普通监听</button>
</template>
~~~
3. 侦听器还可以使用数组同时侦听多个源
* 案例
~~~
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
~~~
* 监听reactive定义的 **引用数据** (需要自己开启 deep:true深度监听)
~~~
setup() {
let rea=reactive({
name:'我是谁',
obj:{
salary:20
}
})
//第五种情况 监听reactive定义的 (引用数据)
watch([()=>rea.name,()=>rea.obj],(qian,hou)=>{
console.log('监听reactive定义的某一些属性---',qian,hou)
},{deep:true})
}
~~~
4. 如果我们希望侦听一个数组或者对象,那么可以使用一个getter函数,并且对可响应对象进行解构
* [监听](https://sfc.vuejs.org/#eNp9Ustu00AU/ZXRbOJIzlhsLScCiS9gwYZh4Tg3sYs9M5oZxwLLUhFCYgEq3SAeEjvaDUJULGDB59Sh/EXv2G7atFUteew795xznzV9oBRbl0BDGplEZ8oSA7ZUMy4IyQoltSU1qWKbpL6GOLHZGkhDlloWZIS8kcO5lyRSGEsysZSPLnBTckHxahEXEHJaVRWnfirn8wxM+ITTF2hyajl9SprxVmxZCuRJQZI0Fit4CKC8cd25dkKwQYmp0qQeqqFQp9K445rK7QouMcyU0+f4cLolBwE5/fPu9O/nf18O2/ff2zc/h1ikPfm62T9uDz62bz+cHR+1B4f+5tWvs6OXW6RfLzDj0OoSUIvbrn+eN57OvJoxdjV+M/Y9AdXjOPdlvsAPgoY0XUdlDiyXq12IP7o36qvEMwr6ueHE0LBQqDy20M0v6sdI6nonYldeFPTOaF5aiy26n+RZ8mzKad8rTmebTz/+7w/VR0EPu4vihuRov0/a199u0Fyi2+SoT/vdmhSxYntGCty/rmo+OAynIRn6wCkumrM5Ta1VJgwCs0zc1u4ZJvUqwD+mS2GzAhiYYjLXsjKgUdgt16VGgJdr0BMNYgEa9F2a16A3dIc9aWhzDjNWOL4=)获取普通对象利用解构
![](https://img.kancloud.cn/a6/5a/a65adb378361703ab54a147bb245fe19_852x127.png)
~~~html
<script setup>
import { watch,reactive } from 'vue'
const infoReactive = reactive({name:"www",hobbies:["z","t"] })
function changeDeep(){
infoReactive.hobbies.push("zt")
}
function change(){
infoReactive.name = "yyyy"
}
// 不会监听到hobbies 属性变化触发,想要监听到,{deep:true}
watch(()=>({...infoReactive}),(newVal,oldVal)=>{
console.log(newVal,oldVal,'1')
})
</script>
<template>
<p>
{{infoReactive}}
</p>
<button @click="change">普通监听</button>
<button @click="changeDeep">深入监听</button>
</template>
~~~
5. 可以设置deep (深度监听) 其值为`true`和`false`。还可以设置`immediate `(是否以当前值执行回调函数) 其值为`true`和`false`。
>[info] ## watch注意点问题
1.监听reactive定义的**响应式数据**,会强制开启深度监听(deep:true),无法获取正确的`oldvalue`(变化前的值)。
2.监听`reactive`定义的**响应式数据中的某个属性**(对象形式)时,不会强制开启深度监听,需要自己手动设置(deep:true)才会有效果。
>[info] ## 官网
[## watch](https://cn.vuejs.org/api/reactivity-core.html#watch)
[侦听器](https://cn.vuejs.org/guide/essentials/watchers.html#basic-example)
- 官网给的工具
- 声明vue2 和 vue3
- 指令速览
- Mustache -- 语法
- v-once -- 只渲染一次
- v-text -- 插入文本
- v-html -- 渲染html
- v-pre -- 显示原始的Mustache标签
- v-cloak -- 遮盖
- v-memo(新)-- 缓存指定值
- v-if/v-show -- 条件渲染
- v-for -- 循环
- v-bind -- 知识
- v-bind -- 修饰符
- v-on -- 点击事件
- v-model -- 双向绑定
- 其他基础知识速览
- 快速使用
- 常识知识点
- key -- 作用 (后续要更新)
- computed -- 计算属性
- watch -- 侦听
- 防抖和节流
- vue3 -- 生命周期
- vue-cli 和 vite 项目搭建方法
- vite -- 导入动态图片
- 组件
- 单文件组件 -- SFC
- 组件通信 -- porp
- 组件通信 -- $emit
- 组件通信 -- Provide / Inject
- 组件通信 -- 全局事件总线mitt库
- 插槽 -- slot
- 整体使用案例
- 动态组件 -- is
- keep-alive
- 分包 -- 异步组价
- mixin -- 混入
- v-model-- 组件
- 使用计算属性
- v-model -- 自定义修饰符
- Suspense -- 实验属性
- Teleport -- 指定挂载
- 组件实例 -- $ 属性
- Option API VS Composition API
- Setup -- 组合API 入口
- api -- reactive
- api -- ref
- 使用ref 和 reactive 场景
- api -- toRefs 和 toRef
- api -- readonly
- 判断性 -- API
- 功能性 -- API
- api -- computed
- api -- $ref 使用
- api -- 生命周期
- Provide 和 Inject
- watch
- watchEffect
- watch vs. watchEffect
- 简单使用composition Api
- 响应性语法糖
- css -- 功能
- 修改css -- :deep() 和 var
- Vue3.2 -- 语法
- ts -- vscode 配置
- attrs/emit/props/expose/slots -- 使用
- props -- defineProps
- props -- defineProps Ts
- emit -- defineEmits
- emit -- defineEmits Ts
- $ref -- defineExpose
- slots/attrs -- useSlots() 和 useAttrs()
- 自定义指令
- Vue -- 插件
- Vue2.x 和 Vue3.x 不同点
- $children -- 移除
- v-for 和 ref
- attribute 强制行为
- 按键修饰符
- v-if 和 v-for 优先级
- 组件使用 v-model -- 非兼容
- 组件
- h -- 函数
- jsx -- 编写
- Vue -- Router
- 了解路由和vue搭配
- vueRouter -- 简单实现
- 安装即使用
- 路由懒加载
- router-view
- router-link
- 路由匹配规则
- 404 页面配置
- 路由嵌套
- 路由组件传参
- 路由重定向和别名
- 路由跳转方法
- 命名路由
- 命名视图
- Composition API
- 路由守卫
- 路由元信息
- 路由其他方法 -- 添加/删除/获取
- 服务器配置映射
- 其他
- Vuex -- 状态管理
- Option Api -- VUEX
- composition API -- VUEX
- module -- VUEX
- 刷新后vuex 数据同步
- 小技巧
- Pinia -- 状态管理
- 开始使用
- pinia -- state
- pinia -- getter
- pinia -- action
- pinia -- 插件 ??
- Vue 源码解读
- 开发感悟
- 练手项目