Vue3中watchEffect和watch的基礎(chǔ)應(yīng)用詳解
watchEffect
watchEffect會自動收集函數(shù)里面變量的響應(yīng)式依賴。在初始化的時候watchEffect會自動執(zhí)行一次(這是無法阻止的),之后watchEffect會根據(jù)收集到的響應(yīng)式依賴,在變量發(fā)生改變時就會被觸發(fā)。
接下來看官方的描述:
wactchEffect:立即運行一個函數(shù),同時響應(yīng)式地追蹤其依賴,并在依賴更改時重新執(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)式對像,它會在頁面初始化的時候就執(zhí)行一次用于收集name的響應(yīng)式依賴,changeName事件被觸發(fā)時,name被改變了,對應(yīng)的就會觸發(fā)watchEffect;當(dāng)changeAge觸發(fā)時,因為并沒有在watchEffect中使用age,所以watchEffect沒有收集到對應(yīng)的響應(yīng)式依賴,watchEffect就不會被觸發(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:自動收集響應(yīng)式依賴,默認(rèn)初始化就會執(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)聽
watchEffect會返回一個函數(shù),這個函數(shù)可以用于停止對響應(yīng)式對象的監(jiān)聽,下面例子中當(dāng)age > 25是就會停止監(jiān)聽:
?<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:自動收集響應(yīng)式依賴,默認(rèn)初始化就會執(zhí)行一次
? ? ?const name = ref("李四")
? ? ?const age = ref(18)
??
? ? ?// wacthEffect會返回一個函數(shù),這個函數(shù)可用于停止所有的wacthEffect的偵聽
? ? ?const stop = watchEffect(() => {
? ? ? ?console.log("userInfo:", name.value,age.value);
? ? })
??
? ? ?const changeName = () => name.value += "1"
? ? ?const changeAge = () => {
? ? ? ?age.value += 1
? ? ? ?// 當(dāng) age > 25 時停止偵聽
? ? ? ?if(age.value > 25) stop()
? ? }
??
? ? ?return {
? ? ? ?name,
? ? ? ?age,
? ? ? ?changeName,
? ? ? ?changeAge
? ? }
? },
?});
?</script>
??
?<style scoped></style>清除副作用
在使用監(jiān)聽的時候我們可能會向服務(wù)器發(fā)送請求,當(dāng)監(jiān)聽的數(shù)據(jù)頻繁變化時,這種請求就會頻繁觸發(fā),這無疑極大的浪費了服務(wù)器性能。watchEffect第一個參數(shù)就是要運行的副作用函數(shù)。這個副作用函數(shù)的參數(shù)也是一個函數(shù),用來注冊清理回調(diào),下面是官方給的例子:
?watchEffect(async (onCleanup) => {
? ?const { response, cancel } = doAsyncWork(id.value)
? ?// `cancel` 會在 `id` 更改時調(diào)用
? ?// 以便取消之前
? ?// 未完成的請求
? ?onCleanup(cancel)
? ?data.value = await response
?})執(zhí)行時機
有時候我們需要去監(jiān)聽dome的變化,通過ref拿到的dome在watchEffect第一次執(zhí)行時是null,這是因為此時dome還未渲染完成。watchEffec的第二個參數(shù)是一個可選項,其中flush可以用來調(diào)整watchEffect執(zhí)行時機。
下面是官方對flush的描述:
默認(rèn)情況下,偵聽器將在組件渲染之前執(zhí)行。設(shè)置 flush: 'post' 將會使偵聽器延遲到組件渲染之后再執(zhí)行。在某些特殊情況下 (例如要使緩存失效),可能有必要在響應(yīng)式依賴發(fā)生改變時立即觸發(fā)偵聽器。這可以通過設(shè)置 flush: 'sync' 來實現(xiàn)。然而,該設(shè)置應(yīng)謹(jǐn)慎使用,因為如果有多個屬性同時更新,這將導(dǎo)致一些性能和數(shù)據(jù)一致性的問題。
舉個栗子:
默認(rèn)’pre‘:偵聽器會在組件渲染前執(zhí)行,控制臺會輸出兩次,第一次為null,第二次是頁面渲染完成成功獲取到組件的時候,會輸出組件的引用:
?<template>
? ?<div id="app">
? ? ?<h2 ref="name">張三</h2>
? ?</div>
?</template>
??
?<script>
?import { watchEffect, defineComponent, ref } from "vue";
??
?export default defineComponent({
? ?setup() {
? ? ? ?// 執(zhí)行時機(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é)果截圖如下: 我們可以在控制臺上看到wathcEffect在渲染完成之前執(zhí)行了一次,此時的name為null,當(dāng)渲染完成之后name的值發(fā)生了改變,watchEffect再次執(zhí)行,輸出這個節(jié)點:

修改為flush: 'post':它將會使偵聽器延遲到組件渲染之后再執(zhí)行。在某些特殊情況下 (例如要使緩存失效),可能有必要在響應(yīng)式依賴發(fā)生改變時立即觸發(fā)偵聽器。所以控制臺只會輸出一次,輸出的是組件的引用:
?<template>
? ?<div id="app">
? ? ?<h2 ref="name">張三</h2>
? ?</div>
?</template>
??
?<script>
?import { watchEffect, defineComponent, ref } from "vue";
??
?export default defineComponent({
? ?setup() {
? ? ? ?// 執(zhí)行時機(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í)行之后就不會觸發(fā)一次無意義的監(jiān)聽了

watch
watch是一個偵聽器,默認(rèn)是懶偵聽的,即僅在偵聽源發(fā)生變化時才執(zhí)行回調(diào)函數(shù)。先貼官方文檔
watch需要偵聽特定的數(shù)據(jù)源,并在回調(diào)函數(shù)中執(zhí)行副作用;- 默認(rèn)情況下
watch是懶監(jiān)聽,只有在被監(jiān)聽的數(shù)據(jù)源發(fā)生變化的時候才會執(zhí)行回調(diào);
watch與watchEffect的區(qū)別
watch默認(rèn)不會初始化立即執(zhí)行;watch有更具體的說明那些狀態(tài)發(fā)生變化是觸發(fā)偵聽器的執(zhí)行;watch能夠訪問偵聽狀態(tài)變化前后的值;
類型參數(shù)
下面是官網(wǎng)對watch類型的描述:
?// 偵聽單個來源 ?function watch<T>( ? ?source: WatchSource<T>, ? ?callback: WatchCallback<T>, ? ?options?: WatchOptions ?): StopHandle ?? ?// 偵聽多個來源 ?function watch<T>( ? ?sources: WatchSource<T>[], ? ?callback: WatchCallback<T[]>, ? ?options?: WatchOptions ?): StopHandle
watch一共有三個參數(shù),分別是:source、callback和options,options為可選參數(shù)。
soure
soure是一個WatchSource<T>類型,該類型規(guī)定了soure可以是ref對象、reactive對象、數(shù)組對象、函數(shù)(getter)和普通對象:
?type WatchSource<T> = ? ?| Ref<T> // ref ? ?| (() => T) // getter ? ?| T extends object ? ?? T ? : never // 響應(yīng)式對象
callback
watch的第二個參數(shù)callback是watch執(zhí)行的回調(diào),這個函數(shù)有三個參數(shù),分別是vaule(新值)、oldValue(舊值)、onCleanup函數(shù)(用于清除副作用),下面是官網(wǎng)對于watch回調(diào)函數(shù)的描述:
?type WatchCallback<T> = ( ? ?value: T, ? ?oldValue: T, ? ?onCleanup: (cleanupFn: () => void) => void ?) => void
options
options是可選配置項。我們通過下面接口的描述看到它是繼承至WatchEffectOptions的。immediate可以控制watch在組件初始化是是否執(zhí)行,默認(rèn)值是false。
deep是控制是否開啟深度監(jiān)聽的參數(shù),watch在監(jiān)聽雜的對象時只對表層進行監(jiān)聽,默認(rèn)值是false,如果對象的屬性還是一個對像,那么這個對象只要地址不改變watch是不會觸發(fā)的,通過deep: true可以監(jiān)聽到深層對象的改變,需要注意的是:1、當(dāng)watch監(jiān)聽的是一個reactive對象時會自動開啟深度監(jiān)聽;2、如果回調(diào)函數(shù)是因為深度監(jiān)聽的變更而觸發(fā)的,那么value和oldValue將會是同一個對象。
flush和watchEffect一樣的flush,用于控制觸發(fā)實機,默認(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
?}基本使用
我將通過source傳參的不同來舉例watch的基本使用:
傳入ref響應(yīng)式對象
當(dāng)監(jiān)聽的是ref對象時,callback的value和oldValue獲得的是ref對象對應(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對象,newValue和oldValue是對應(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>偵聽多個來源,callback會接收兩個數(shù)組,對應(yīng)的順序是偵聽數(shù)組的順序,為了更直觀我做了解構(gòu),也可以寫成(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() {
? ? ? // 傳入多個對象,newValue和oldValue是對應(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對象,callback對應(yīng)的value和oldValue都將是reactive對象,下面userInfo是一個reactive對象,所以newValue和oldValue都會是reactive對象:
?<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對象
? ? ? 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ù)傳參方式對reactive進行解構(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對象可以在傳入時對它進行解構(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ù)作為源時,回調(diào)只在此函數(shù)的返回值變化時才會觸發(fā)。如果你想讓回調(diào)在深層級變更時也能觸發(fā),你需要使用 { deep: true } 強制偵聽器進入深層級模式。在深層級模式時,如果回調(diào)函數(shù)由于深層級的變更而被觸發(fā),那么新值和舊值將是同一個對象。"
?<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的資料請關(guān)注腳本之家其它相關(guān)文章!
- Vue3中Watch、Watcheffect、Computed的使用和區(qū)別解析
- vue3 watch和watchEffect的使用以及有哪些區(qū)別
- Vue3.0監(jiān)聽器watch與watchEffect詳解
- vue3中的watch和watchEffect實例詳解
- 淺談Vue3中watchEffect的具體用法
- Vue3?中?watch?與?watchEffect?區(qū)別及用法小結(jié)
- Vue3中watchEffect高級偵聽器的具體使用
- VUE3中watch和watchEffect的用法詳解
- 一文搞懂Vue3中watchEffect偵聽器的使用
- Vue3中watch與watchEffect使用方法詳解
相關(guān)文章
Vue3實現(xiàn)動態(tài)導(dǎo)入Excel表格數(shù)據(jù)的方法詳解
在開發(fā)工作過程中,我們會遇到各種各樣的表格數(shù)據(jù)導(dǎo)入,動態(tài)數(shù)據(jù)導(dǎo)入可以減少人為操作,減少出錯。本文為大家介紹了Vue3實現(xiàn)動態(tài)導(dǎo)入Excel表格數(shù)據(jù)的方法,需要的可以參考一下2022-11-11
詳解webpack + vue + node 打造單頁面(入門篇)
本篇文章主要介紹了詳解webpack + vue + node 打造單頁面(入門篇) ,非常具有實用價值,需要的朋友可以參考下2017-09-09
vue中beforeRouteLeave實現(xiàn)頁面回退不刷新的示例代碼
這篇文章主要介紹了vue中beforeRouteLeave實現(xiàn)頁面回退不刷新的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
vue router使用query和params傳參的使用和區(qū)別
本篇文章主要介紹了vue router使用query和params傳參的使用和區(qū)別,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
前端Vue3項目打包成Docker鏡像運行的詳細(xì)步驟
將Vue3項目打包、編寫Dockerfile、構(gòu)建Docker鏡像和運行容器是部署Vue3項目到Docker的主要步驟,這篇文章主要介紹了前端Vue3項目打包成Docker鏡像運行的詳細(xì)步驟,需要的朋友可以參考下2024-09-09

