🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
>[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)