Vue3中watchEffect和watch的基礎(chǔ)應(yīng)用詳解
watchEffect
watchEffect
會(huì)自動(dòng)收集函數(shù)里面變量的響應(yīng)式依賴。在初始化的時(shí)候watchEffect
會(huì)自動(dòng)執(zhí)行一次(這是無(wú)法阻止的),之后watchEffect
會(huì)根據(jù)收集到的響應(yīng)式依賴,在變量發(fā)生改變時(shí)就會(huì)被觸發(fā)。
接下來(lái)看官方的描述:
wactchEffect
:立即運(yùn)行一個(gè)函數(shù),同時(shí)響應(yīng)式地追蹤其依賴,并在依賴更改時(shí)重新執(zhí)行。
類型
?function watchEffect( ? ?effect: (onCleanup: OnCleanup) => void, ? ?options?: WatchEffectOptions ?): StopHandle ?? ?type OnCleanup = (cleanupFn: () => void) => void ?? ?interface WatchEffectOptions { ? ?flush?: 'pre' | 'post' | 'sync' // 默認(rèn):'pre' ? ?onTrack?: (event: DebuggerEvent) => void ? ?onTrigger?: (event: DebuggerEvent) => void ?} ?? ?type StopHandle = () => void
基本使用
下面例子中watchEffect
中只有name
是響應(yīng)式對(duì)像,它會(huì)在頁(yè)面初始化的時(shí)候就執(zhí)行一次用于收集name
的響應(yīng)式依賴,changeName
事件被觸發(fā)時(shí),name
被改變了,對(duì)應(yīng)的就會(huì)觸發(fā)watchEffect
;當(dāng)changeAge
觸發(fā)時(shí),因?yàn)椴](méi)有在watchEffect
中使用age
,所以watchEffect
沒(méi)有收集到對(duì)應(yīng)的響應(yīng)式依賴,watchEffect
就不會(huì)被觸發(fā)。
?<template> ? ?<div id="app"> ? ? ?<h2>{{ name }}</h2> ? ? ?<h2>{{ age }}</h2> ? ? ?<button @click="changeName">修改name</button> ? ? ?<button @click="changeAge">修改Age</button> ? ?</div> ?</template> ?? ?<script> ?import { watchEffect, defineComponent, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ?//watchEffect:自動(dòng)收集響應(yīng)式依賴,默認(rèn)初始化就會(huì)執(zhí)行一次 ? ? ?const name = ref("李四") ? ? ?const age = ref(18) ?? ? ? ?watchEffect(() => { ? ? ? ?console.log("name:", name.value); ? ? }) ? ? ?const changeName = () => name.value += "1" ? ? ?const changeAge = () => age.value += 1 ?? ? ? ?return { ? ? ? ?name, ? ? ? ?age, ? ? ? ?changeName, ? ? ? ?changeAge ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
停止監(jiān)聽(tīng)
watchEffect
會(huì)返回一個(gè)函數(shù),這個(gè)函數(shù)可以用于停止對(duì)響應(yīng)式對(duì)象的監(jiān)聽(tīng),下面例子中當(dāng)age > 25
是就會(huì)停止監(jiān)聽(tīng):
?<template> ? ?<div id="app"> ? ? ?<h2>{{ name }}</h2> ? ? ?<h2>{{ age }}</h2> ? ? ?<button @click="changeName">修改name</button> ? ? ?<button @click="changeAge">修改Age</button> ? ?</div> ?</template> ?? ?<script> ?import { watchEffect, defineComponent, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ?//watchEffect:自動(dòng)收集響應(yīng)式依賴,默認(rèn)初始化就會(huì)執(zhí)行一次 ? ? ?const name = ref("李四") ? ? ?const age = ref(18) ?? ? ? ?// wacthEffect會(huì)返回一個(gè)函數(shù),這個(gè)函數(shù)可用于停止所有的wacthEffect的偵聽(tīng) ? ? ?const stop = watchEffect(() => { ? ? ? ?console.log("userInfo:", name.value,age.value); ? ? }) ?? ? ? ?const changeName = () => name.value += "1" ? ? ?const changeAge = () => { ? ? ? ?age.value += 1 ? ? ? ?// 當(dāng) age > 25 時(shí)停止偵聽(tīng) ? ? ? ?if(age.value > 25) stop() ? ? } ?? ? ? ?return { ? ? ? ?name, ? ? ? ?age, ? ? ? ?changeName, ? ? ? ?changeAge ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
清除副作用
在使用監(jiān)聽(tīng)的時(shí)候我們可能會(huì)向服務(wù)器發(fā)送請(qǐng)求,當(dāng)監(jiān)聽(tīng)的數(shù)據(jù)頻繁變化時(shí),這種請(qǐng)求就會(huì)頻繁觸發(fā),這無(wú)疑極大的浪費(fèi)了服務(wù)器性能。watchEffect
第一個(gè)參數(shù)就是要運(yùn)行的副作用函數(shù)。這個(gè)副作用函數(shù)的參數(shù)也是一個(gè)函數(shù),用來(lái)注冊(cè)清理回調(diào),下面是官方給的例子:
?watchEffect(async (onCleanup) => { ? ?const { response, cancel } = doAsyncWork(id.value) ? ?// `cancel` 會(huì)在 `id` 更改時(shí)調(diào)用 ? ?// 以便取消之前 ? ?// 未完成的請(qǐng)求 ? ?onCleanup(cancel) ? ?data.value = await response ?})
執(zhí)行時(shí)機(jī)
有時(shí)候我們需要去監(jiān)聽(tīng)dome
的變化,通過(guò)ref
拿到的dome
在watchEffect
第一次執(zhí)行時(shí)是null
,這是因?yàn)榇藭r(shí)dome
還未渲染完成。watchEffec
的第二個(gè)參數(shù)是一個(gè)可選項(xiàng),其中flush
可以用來(lái)調(diào)整watchEffect
執(zhí)行時(shí)機(jī)。
下面是官方對(duì)flush
的描述:
默認(rèn)情況下,偵聽(tīng)器將在組件渲染之前執(zhí)行。設(shè)置 flush: 'post'
將會(huì)使偵聽(tīng)器延遲到組件渲染之后再執(zhí)行。在某些特殊情況下 (例如要使緩存失效),可能有必要在響應(yīng)式依賴發(fā)生改變時(shí)立即觸發(fā)偵聽(tīng)器。這可以通過(guò)設(shè)置 flush: 'sync'
來(lái)實(shí)現(xiàn)。然而,該設(shè)置應(yīng)謹(jǐn)慎使用,因?yàn)槿绻卸鄠€(gè)屬性同時(shí)更新,這將導(dǎo)致一些性能和數(shù)據(jù)一致性的問(wèn)題。
舉個(gè)栗子:
默認(rèn)’pre‘:偵聽(tīng)器會(huì)在組件渲染前執(zhí)行,控制臺(tái)會(huì)輸出兩次,第一次為null
,第二次是頁(yè)面渲染完成成功獲取到組件的時(shí)候,會(huì)輸出組件的引用:
?<template> ? ?<div id="app"> ? ? ?<h2 ref="name">張三</h2> ? ?</div> ?</template> ?? ?<script> ?import { watchEffect, defineComponent, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ? ?// 執(zhí)行時(shí)機(jī)(flush):'pre' | 'post' | 'sync' // 默認(rèn)'pre' ? ? ? ?const name = ref(null) ? ? ? ?watchEffect(() => { ? ? ? ? ? ?console.log("nameDome:", name.value); ? ? ? }) ? ? ?return { ? ? ? ?name ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
執(zhí)行結(jié)果截圖如下: 我們可以在控制臺(tái)上看到wathcEffect
在渲染完成之前執(zhí)行了一次,此時(shí)的name
為null
,當(dāng)渲染完成之后name
的值發(fā)生了改變,watchEffect
再次執(zhí)行,輸出這個(gè)節(jié)點(diǎn):
修改為flush: 'post'
:它將會(huì)使偵聽(tīng)器延遲到組件渲染之后再執(zhí)行。在某些特殊情況下 (例如要使緩存失效),可能有必要在響應(yīng)式依賴發(fā)生改變時(shí)立即觸發(fā)偵聽(tīng)器。所以控制臺(tái)只會(huì)輸出一次,輸出的是組件的引用:
?<template> ? ?<div id="app"> ? ? ?<h2 ref="name">張三</h2> ? ?</div> ?</template> ?? ?<script> ?import { watchEffect, defineComponent, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ? ?// 執(zhí)行時(shí)機(jī)(flush):'pre' | 'post' | 'sync' // 默認(rèn)'pre' ? ? ? ?const name = ref(null) ? ? ? ?watchEffect(() => { ? ? ? ? ? ?console.log("nameDome:", name.value); ? ? ? }, { ? ? ? ? ?flush: 'post' ? ? ? }) ?? ? ? ?return { ? ? ? ?name ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
執(zhí)行結(jié)果截圖如下: ,延后執(zhí)行之后就不會(huì)觸發(fā)一次無(wú)意義的監(jiān)聽(tīng)了
watch
watch
是一個(gè)偵聽(tīng)器,默認(rèn)是懶偵聽(tīng)的,即僅在偵聽(tīng)源發(fā)生變化時(shí)才執(zhí)行回調(diào)函數(shù)。先貼官方文檔
watch
需要偵聽(tīng)特定的數(shù)據(jù)源,并在回調(diào)函數(shù)中執(zhí)行副作用;- 默認(rèn)情況下
watch
是懶監(jiān)聽(tīng),只有在被監(jiān)聽(tīng)的數(shù)據(jù)源發(fā)生變化的時(shí)候才會(huì)執(zhí)行回調(diào);
watch與watchEffect的區(qū)別
watch
默認(rèn)不會(huì)初始化立即執(zhí)行;watch
有更具體的說(shuō)明那些狀態(tài)發(fā)生變化是觸發(fā)偵聽(tīng)器的執(zhí)行;watch
能夠訪問(wèn)偵聽(tīng)狀態(tài)變化前后的值;
類型參數(shù)
下面是官網(wǎng)對(duì)watch
類型的描述:
?// 偵聽(tīng)單個(gè)來(lái)源 ?function watch<T>( ? ?source: WatchSource<T>, ? ?callback: WatchCallback<T>, ? ?options?: WatchOptions ?): StopHandle ?? ?// 偵聽(tīng)多個(gè)來(lái)源 ?function watch<T>( ? ?sources: WatchSource<T>[], ? ?callback: WatchCallback<T[]>, ? ?options?: WatchOptions ?): StopHandle
watch
一共有三個(gè)參數(shù),分別是:source
、callback
和options
,options
為可選參數(shù)。
soure
soure
是一個(gè)WatchSource<T>
類型,該類型規(guī)定了soure
可以是ref
對(duì)象、reactive
對(duì)象、數(shù)組對(duì)象、函數(shù)(getter)和普通對(duì)象:
?type WatchSource<T> = ? ?| Ref<T> // ref ? ?| (() => T) // getter ? ?| T extends object ? ?? T ? : never // 響應(yīng)式對(duì)象
callback
watch
的第二個(gè)參數(shù)callback
是watch
執(zhí)行的回調(diào),這個(gè)函數(shù)有三個(gè)參數(shù),分別是vaule
(新值)、oldValue
(舊值)、onCleanup
函數(shù)(用于清除副作用),下面是官網(wǎng)對(duì)于watch
回調(diào)函數(shù)的描述:
?type WatchCallback<T> = ( ? ?value: T, ? ?oldValue: T, ? ?onCleanup: (cleanupFn: () => void) => void ?) => void
options
options
是可選配置項(xiàng)。我們通過(guò)下面接口的描述看到它是繼承至WatchEffectOptions
的。immediate
可以控制watch
在組件初始化是是否執(zhí)行,默認(rèn)值是false
。
deep
是控制是否開(kāi)啟深度監(jiān)聽(tīng)的參數(shù),watch
在監(jiān)聽(tīng)雜的對(duì)象時(shí)只對(duì)表層進(jìn)行監(jiān)聽(tīng),默認(rèn)值是false
,如果對(duì)象的屬性還是一個(gè)對(duì)像,那么這個(gè)對(duì)象只要地址不改變watch
是不會(huì)觸發(fā)的,通過(guò)deep: true
可以監(jiān)聽(tīng)到深層對(duì)象的改變,需要注意的是:1、當(dāng)watch監(jiān)聽(tīng)的是一個(gè)reactive
對(duì)象時(shí)會(huì)自動(dòng)開(kāi)啟深度監(jiān)聽(tīng);2、如果回調(diào)函數(shù)是因?yàn)樯疃缺O(jiān)聽(tīng)的變更而觸發(fā)的,那么value
和oldValue
將會(huì)是同一個(gè)對(duì)象。
flush
和watchEffect
一樣的flush
,用于控制觸發(fā)實(shí)機(jī),默認(rèn)pre
,在組件渲染之前執(zhí)行,下面是官網(wǎng)的描述:
?interface WatchOptions extends WatchEffectOptions { ? ?immediate?: boolean // 默認(rèn):false ? ?deep?: boolean // 默認(rèn):false ? ?flush?: 'pre' | 'post' | 'sync' // 默認(rèn):'pre' ? ?onTrack?: (event: DebuggerEvent) => void ? ?onTrigger?: (event: DebuggerEvent) => void ?}
基本使用
我將通過(guò)source
傳參的不同來(lái)舉例watch
的基本使用:
傳入ref
響應(yīng)式對(duì)象
當(dāng)監(jiān)聽(tīng)的是ref
對(duì)象時(shí),callback
的value
和oldValue
獲得的是ref
對(duì)象對(duì)應(yīng)的value
值:
?<template> ? ?<div id="app"> ? ? ?<h2>{{ name }}</h2> ? ? ?<button @click="changeName">改變用戶數(shù)據(jù)</button> ? ?</div> ?</template> ?? ?<script> ?import { watch, defineComponent, reactive, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ? // 傳入ref對(duì)象,newValue和oldValue是對(duì)應(yīng)的value值 ? ? ? const name = ref('張三') ? ? ? watch(name, (newVlaue,oldValue) => { ? ? ? ? console.log('name:',newVlaue,oldValue); ? ? ? }) ? ? ?const changeName = ()=>{ ? ? ? ?name.value += "1" ? ? } ? ? ?return { ? ? ? ?name, ? ? ? ?changeName ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
偵聽(tīng)多個(gè)來(lái)源,callback
會(huì)接收兩個(gè)數(shù)組,對(duì)應(yīng)的順序是偵聽(tīng)數(shù)組的順序,為了更直觀我做了解構(gòu),也可以寫(xiě)成(newValue,oldValue)
的形式:
?<template> ? ?<div id="app"> ? ? ?<h2>{{ name }}</h2> ? ? ?<h2>{{ age }}</h2> ? ? ?</h2> ? ? ?<button @click="changeInfo">改變用戶數(shù)據(jù)</button> ? ?</div> ?</template> ?? ?<script> ?import { watch, defineComponent, reactive, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ? // 傳入多個(gè)對(duì)象,newValue和oldValue是對(duì)應(yīng)的value值 ? ? ? const name = ref('張三') ? ? ? const age = ref(18) ? ? ? watch([name,age], ([newName,newAge],[oldName,oldAge]) => { ? ? ? ? console.log('new:',newName,newAge,'old',oldName,oldAge); ? ? ? }) ? ? ?const changeInfo = ()=>{ ? ? ? ?name.value += "1" ? ? ? ?age.value += 1 ? ? } ? ? ?return { ? ? ? ?name, ? ? ? ?age, ? ? ? ?changeInfo ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
傳入reactive
對(duì)象,callback
對(duì)應(yīng)的value
和oldValue
都將是reactive
對(duì)象,下面userInfo
是一個(gè)reactive
對(duì)象,所以newValue
和oldValue
都會(huì)是reactive
對(duì)象:
?<template> ? ?<div id="app"> ? ? ?<h2>{{ userInfo.name }}</h2> ? ? ?<h2>{{ userInfo.age }}</h2> ? ? ?<button @click="changeInfo">改變用戶數(shù)據(jù)</button> ? ?</div> ?</template> ?? ?<script> ?import { watch, defineComponent, reactive, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ?const userInfo = reactive({ name: '張三', age: 18 }) ? ? ?// 傳入reactive對(duì)象 ? ? ? watch(userInfo, (newValue, oldValue) => { ? ? ? ? ? console.log('userInfo',newValue,oldValue); ? ? ? }) ? ? ?const changeInfo = ()=>{ ? ? ? ?userInfo.name += "1" ? ? ? ?userInfo.age += 1 ? ? } ? ? ?return { ? ? ? ?userInfo, ? ? ? ?changeInfo ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
如果我們不希望得到響應(yīng)式的newValue
和oldValue
,那么我們可以使用getter
函數(shù)傳參方式對(duì)reactive
進(jìn)行解構(gòu):
?<template> ? ?<div id="app"> ? ? ?<h2>{{ userInfo.name }}</h2> ? ? ?<h2>{{ userInfo.age }}</h2> ? ? ?<button @click="changeInfo">改變用戶數(shù)據(jù)</button> ? ?</div> ?</template> ?? ?<script> ?import { watch, defineComponent, reactive, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ?const userInfo = reactive({ name: '張三', age: 18 }) ? ? ?// 如果不希望newValue和oldValue是reactive對(duì)象可以在傳入時(shí)對(duì)它進(jìn)行解構(gòu) ? ? ?watch(() => { return {...userInfo} }, (newValue, oldValue) => { ? ? ? ?console.log('userInfo:',newValue,oldValue); ? ? }) ? ? ?const changeInfo = ()=>{ ? ? ? ?userInfo.name += "1" ? ? ? ?userInfo.age += 1 ? ? } ? ? ?return { ? ? ? ?userInfo, ? ? ? ?changeInfo ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
傳入getter函數(shù),下面摘自官網(wǎng)的描述:"當(dāng)使用 getter 函數(shù)作為源時(shí),回調(diào)只在此函數(shù)的返回值變化時(shí)才會(huì)觸發(fā)。如果你想讓回調(diào)在深層級(jí)變更時(shí)也能觸發(fā),你需要使用 { deep: true }
強(qiáng)制偵聽(tīng)器進(jìn)入深層級(jí)模式。在深層級(jí)模式時(shí),如果回調(diào)函數(shù)由于深層級(jí)的變更而被觸發(fā),那么新值和舊值將是同一個(gè)對(duì)象。"
?<template> ? ?<div id="app"> ? ? ?<h2>{{ userInfo.name }}</h2> ? ? ?<h2>{{ userInfo.age }}</h2> ? ? ?<button @click="changeInfo">改變用戶數(shù)據(jù)</button> ? ?</div> ?</template> ?? ?<script> ?import { watch, defineComponent, reactive, ref } from "vue"; ?? ?export default defineComponent({ ? ?setup() { ? ? ?const userInfo = reactive({ name: '張三', age: 18 }) ?? ? ? ?// 二、傳入getter函數(shù)形式 ? ? ?watch(() => userInfo.name, (newVlaue,oldValue) => { ? ? ? ?console.log('newValue',newVlaue,'oldValue',oldValue) ? ? }) ?? ? ? ?const changeInfo = ()=>{ ? ? ? ?userInfo.name += "1" ? ? ? ?userInfo.age += 1 ? ? } ? ? ?return { ? ? ? ?userInfo, ? ? ? ?changeInfo ? ? } ? }, ?}); ?</script> ?? ?<style scoped></style>
以上就是Vue3中watchEffect和watch的基礎(chǔ)應(yīng)用詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue3 watchEffect和watch的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue2.0+vuex+localStorage代辦事項(xiàng)應(yīng)用實(shí)現(xiàn)詳解
本篇文章給大家分享了一個(gè)用vue2.0+vuex+localStorage代辦事項(xiàng)應(yīng)用實(shí)現(xiàn)的代碼過(guò)程,有興趣的朋友跟著參考學(xué)習(xí)下。2018-05-05Vue 創(chuàng)建組件的兩種方法小結(jié)(必看)
Vue 創(chuàng)建組件的方法有哪些呢?下面小編就為大家分享一篇Vue 創(chuàng)建組件的兩種方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02Vue + Webpack + Vue-loader學(xué)習(xí)教程之功能介紹篇
這篇文章主要介紹了關(guān)于Vue + Webpack + Vue-loader功能介紹的相關(guān)資料,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-03-03解決vite項(xiàng)目Uncaught Syntaxerror:Unexpected token>vue項(xiàng)
這篇文章主要介紹了解決vite項(xiàng)目Uncaught Syntaxerror:Unexpected token>vue項(xiàng)目上線白屏問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03vue3.0+element表格獲取每行數(shù)據(jù)代碼示例
這篇文章主要給大家介紹了關(guān)于vue3.0+element表格獲取每行數(shù)據(jù)的相關(guān)資料,在element-ui中,你可以通過(guò)為表格的行綁定單擊事件來(lái)獲取表格中的一行數(shù)據(jù),需要的朋友可以參考下2023-09-09Vue3中Slot插槽透?jìng)?二次封裝Arco的table組件詳解
這篇文章主要介紹了Vue3中Slot插槽透?jìng)?二次封裝Arco的table組件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04