>[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==)**

~~~
<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=)獲取普通對象利用解構

~~~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 源碼解讀
- 開發感悟
- 練手項目