VUE3數(shù)據(jù)的偵聽超詳細講解
前言
偵聽數(shù)據(jù)變化也是組件里的一項重要工作,比如偵聽路由變化、偵聽參數(shù)變化等等。
Vue 3 在保留原來的 watch 功能之外,還新增了一個 watchEffect 幫助更簡單的進行偵聽。
watch
在 Vue 3 ,新版的 watch 和 Vue 2 的舊版寫法對比,在使用方式上變化非常大!
回顧 Vue 2
在 Vue 2 是這樣用的,和 data 、 methods 都在同級配置:
export default {
data() {
return {
// ...
}
},
// 注意這里,放在 `data` 、 `methods` 同個級別
watch: {
// ...
},
methods: {
// ...
},
}
并且類型繁多,選項式 API 的類型如下:
watch: { [key: string]: string | Function | Object | Array}聯(lián)合類型過多,意味著用法復(fù)雜,下面是個很好的例子,雖然出自 官網(wǎng) 的用法介紹,但過于繁多的用法也反映出來對初學(xué)者不太友好,初次接觸可能會覺得一頭霧水:
export default {
data() {
return {
a: 1,
b: 2,
c: {
d: 4,
},
e: 5,
f: 6,
}
},
watch: {
// 偵聽頂級 Property
a(val, oldVal) {
console.log(`new: ${val}, old: ${oldVal}`)
},
// 字符串方法名
b: 'someMethod',
// 該回調(diào)會在任何被偵聽的對象的 Property 改變時被調(diào)用,不論其被嵌套多深
c: {
handler(val, oldVal) {
console.log('c changed')
},
deep: true,
},
// 偵聽單個嵌套 Property
'c.d': function (val, oldVal) {
// do something
},
// 該回調(diào)將會在偵聽開始之后被立即調(diào)用
e: {
handler(val, oldVal) {
console.log('e changed')
},
immediate: true,
},
// 可以傳入回調(diào)數(shù)組,它們會被逐一調(diào)用
f: [
'handle1',
function handle2(val, oldVal) {
console.log('handle2 triggered')
},
{
handler: function handle3(val, oldVal) {
console.log('handle3 triggered')
},
/* ... */
},
],
},
methods: {
someMethod() {
console.log('b changed')
},
handle1() {
console.log('handle 1 triggered')
},
},
}當然肯定也會有開發(fā)者會覺得這樣選擇多是個好事,選擇適合自己的就好,但筆者還是認為這種寫法對于初學(xué)者來說不是那么友好,有些過于復(fù)雜化,如果一個用法可以適應(yīng)各種各樣的場景,豈不是更妙?
TIP
另外需要注意的是,不能使用箭頭函數(shù)來定義 Watcher 函數(shù) (例如 searchQuery: newValue => this.updateAutocomplete(newValue) )。
因為箭頭函數(shù)綁定了父級作用域的上下文,所以 this 將不會按照期望指向組件實例, this.updateAutocomplete 將是 undefined 。
Vue 2 也可以通過 this.$watch() 這個 API 的用法來實現(xiàn)對某個數(shù)據(jù)的偵聽,它接受三個參數(shù): source 、 callback 和 options 。
export default {
data() {
return {
a: 1,
}
},
// 生命周期鉤子
mounted() {
this.$watch('a', (newVal, oldVal) => {
// ...
})
},
}由于 this.$watch 的用法和 Vue 3 比較接近,所以這里不做過多的回顧,請直接看 了解 Vue 3 部分。
了解 Vue 3
在 Vue 3 的組合式 API 寫法, watch 是一個可以接受 3 個參數(shù)的函數(shù)(保留了 Vue 2 的 this.$watch 這種用法),在使用層面上簡單了很多。
import { watch } from 'vue'
// 一個用法走天下
watch(
source, // 必傳,要偵聽的數(shù)據(jù)源
callback // 必傳,偵聽到變化后要執(zhí)行的回調(diào)函數(shù)
// options // 可選,一些偵聽選項
)下面的內(nèi)容都基于 Vue 3 的組合式 API 用法展開講解。
API 的 TS 類型
在了解用法之前,先對它的 TS 類型聲明做一個簡單的了解, watch 作為組合式 API ,根據(jù)使用方式有兩種類型聲明:
1.基礎(chǔ)用法的 TS 類型,詳見 基礎(chǔ)用法 部分
// watch 部分的 TS 類型 // ... export declare function watch<T, Immediate extends Readonly<boolean> = false>( source: WatchSource<T>, cb: WatchCallback<T, Immediate extends true ? T | undefined : T>, options?: WatchOptions<Immediate> ): WatchStopHandle // ...
2.批量偵聽的 TS 類型,詳見 批量偵聽 部分
// watch 部分的 TS 類型 // ... export declare function watch< T extends MultiWatchSources, Immediate extends Readonly<boolean> = false >( sources: [...T], cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>, options?: WatchOptions<Immediate> ): WatchStopHandle // MultiWatchSources 是一個數(shù)組 declare type MultiWatchSources = (WatchSource<unknown> | object)[] // ...
但是不管是基礎(chǔ)用法還是批量偵聽,可以看到這個 API 都是接受三個入?yún)ⅲ?/p>

并返回一個可以用來停止偵聽的函數(shù)(詳見:停止偵聽)。
要偵聽的數(shù)據(jù)源
在上面 API 的 TS 類型 已經(jīng)對 watch API 的組成有一定的了解了,這里先對數(shù)據(jù)源的類型和使用限制做下說明。
TIP
如果不提前了解,在使用的過程中可能會遇到 “偵聽了但沒有反應(yīng)” 的情況出現(xiàn)。
另外,這部分內(nèi)容會先圍繞基礎(chǔ)用法展開說明,批量偵聽會在 批量偵聽 部分單獨說明。
watch API 的第 1 個參數(shù) source 是要偵聽的數(shù)據(jù)源,它的 TS 類型如下:
// watch 第 1 個入?yún)⒌?TS 類型 // ... export declare type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T) // ...
可以看到能夠用于偵聽的數(shù)據(jù),是通過 響應(yīng)式 API 定義的變量( Ref ),或者是一個 計算數(shù)據(jù) ( ComputedRef ),或者是一個 getter 函數(shù) ( () => T )。
所以要想定義的 watch 能夠做出預(yù)期的行為,數(shù)據(jù)源必須具備響應(yīng)性或者是一個 getter ,如果只是通過 let 定義一個普通變量,然后去改變這個變量的值,這樣是無法偵聽的。
TIP
如果要偵聽響應(yīng)式對象里面的某個值(這種情況下對象本身是響應(yīng)式,
但它的 property 不是),
需要寫成 getter 函數(shù),
簡單的說就是需要寫成有返回值的函數(shù),
這個函數(shù) return 要偵聽的數(shù)據(jù), e.g. () => foo.bar ,
可以結(jié)合下方 基礎(chǔ)用法 的例子一起理解。
偵聽后的回調(diào)函數(shù)
在上面 API 的 TS 類型 介紹了 watch API 的組成,和數(shù)據(jù)源一樣,先了解一下回調(diào)函數(shù)的定義。
TIP
和數(shù)據(jù)源部分一樣,回調(diào)函數(shù)的內(nèi)容也是會先圍繞基礎(chǔ)用法展開說明,
批量偵聽會在 批量偵聽 部分單獨說明。
watch API 的第 2 個參數(shù) callback 是偵聽到數(shù)據(jù)變化時要做出的行為,它的 TS 類型如下:
// watch 第 2 個入?yún)⒌?TS 類型 // ... export declare type WatchCallback<V = any, OV = any> = ( value: V, oldValue: OV, onCleanup: OnCleanup ) => any // ...
乍一看它有三個參數(shù),但實際上這些參數(shù)不是自己定義的,而是 watch API 傳給的,所以不管用或者不用,它們都在那里:

注意:第一個參數(shù)是新值,第二個才是原來的舊值!
如同其他 JS 函數(shù),在使用 watch 的回調(diào)函數(shù)時,可以對這三個參數(shù)任意命名,比如把 value 命名為覺得更容易理解的 newValue 。
TIP
如果偵聽的數(shù)據(jù)源是一個 引用類型 時( e.g. Object 、 Array 、 Date … ),
value 和 oldValue 是完全相同的,因為指向同一個對象。
另外,默認情況下,watch 是惰性的,也就是只有當被偵聽的數(shù)據(jù)源發(fā)生變化時才執(zhí)行回調(diào)。
基礎(chǔ)用法
來到這里,對 2 個必傳的參數(shù)都有一定的了解了,先看看基礎(chǔ)的用法,也就是日常最常編寫的方案,只需要先關(guān)注前 2 個必傳的參數(shù)。
// 不要忘了導(dǎo)入要用的 API
import {defineComponent,reactive ,watch} from 'vue'
export default defineComponent({
setup(){
//定義一個響應(yīng)式數(shù)據(jù)
const userInfo=reactive({
name:'Petter',
age:18
})
//2s后改變數(shù)據(jù)
setTimeout(()=>{
userInfo.name='tom'
},2000)
/**
* 可以直接偵聽這個響應(yīng)式對象
* callback 的參數(shù)如果不用可以不寫
*/
watch(userInfo,()=>{
console.log('偵聽整個 userInfo ', userInfo.name)
})
/**
* 也可以偵聽對象里面的某個值
* 此時數(shù)據(jù)源需要寫成 getter 函數(shù)
*/
watch(
//數(shù)據(jù)源,getter形式
()=>userInfo.name,
// 回調(diào)函數(shù) callback
(newValue, oldValue) => {
console.log('只偵聽 name 的變化 ', userInfo.name)
console.log('打印變化前后的值', { oldValue, newValue })
}
)
}
})
一般的業(yè)務(wù)場景,基礎(chǔ)用法足以面對。
如果有多個數(shù)據(jù)源要偵聽,并且偵聽到變化后要執(zhí)行的行為一樣,那么可以使用 批量偵聽 。
特殊的情況下,可以搭配 偵聽的選項 做一些特殊的用法,詳見下面部分的內(nèi)容。
批量偵聽
如果有多個數(shù)據(jù)源要偵聽,并且偵聽到變化后要執(zhí)行的行為一樣,第一反應(yīng)可能是這樣來寫:
1.抽離相同的處理行為為公共函數(shù)
2.然后定義多個偵聽操作,傳入這個公共函數(shù)
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
const message = ref<string>('')
const index = ref<number>(0)
// 2s后改變數(shù)據(jù)
setTimeout(() => {
// 來到這里才會觸發(fā) watch 的回調(diào)
message.value = 'Hello World!'
index.value++
}, 2000)
// 抽離相同的處理行為為公共函數(shù)
const handleWatch = (
newValue: string | number,
oldValue: string | number
): void => {
console.log({ newValue, oldValue })
}
// 然后定義多個偵聽操作,傳入這個公共函數(shù)
watch(message, handleWatch)
watch(index, handleWatch)
},
})這樣寫其實沒什么問題,不過除了抽離公共代碼的寫法之外, watch API 還提供了一個批量偵聽的用法,和 基礎(chǔ)用法 的區(qū)別在于,數(shù)據(jù)源和回調(diào)參數(shù)都變成了數(shù)組的形式。
數(shù)據(jù)源:以數(shù)組的形式傳入,里面每一項都是一個響應(yīng)式數(shù)據(jù)。
回調(diào)參數(shù):原來的 value 和 newValue 也都變成了數(shù)組,每個數(shù)組里面的順序和數(shù)據(jù)源數(shù)組排序一致。
可以看下面的這個例子更為直觀:
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup(){
//定義多個數(shù)據(jù)源
const message = ref<string>('')
const index = ref<number>(0)
//2s后改變數(shù)據(jù)
setTimeout(()=>{
message.value = 'Hello World!'
index.value++
},2000)
watch(
//數(shù)據(jù)源改成了數(shù)組
[message, index],
//回調(diào)的入?yún)⒁沧兂闪藬?shù)組,每個數(shù)組里面的順序和數(shù)據(jù)源數(shù)組排序一致
([newMessage, newIndex], [oldMessage, oldIndex])=>{
console.log('message 的變化', { newMessage, oldMessage })
console.log('index 的變化', { newIndex, oldIndex })
}
)
}
})
什么情況下可能會用到批量偵聽呢?比如一個子組件有多個 props ,當有任意一個 prop 發(fā)生變化時,都需要執(zhí)行初始化函數(shù)重置組件的狀態(tài),那么這個時候就可以用上這個功能啦!
TIP
在適當?shù)臉I(yè)務(wù)場景,
也可以使用 watchEffect 來完成批量偵聽,
但請留意 功能區(qū)別 部分的說明。
偵聽的選項
在 API 的 TS 類型 里提到, watch API 還接受第 3 個參數(shù) options ,可選的一些偵聽選項。
它的 TS 類型如下:
// watch 第 3 個入?yún)⒌?TS 類型
// ...
export declare interface WatchOptions<Immediate = boolean>
extends WatchOptionsBase {
immediate?: Immediate
deep?: boolean
}
// ...
// 繼承的 base 類型
export declare interface WatchOptionsBase extends DebuggerOptions {
flush?: 'pre' | 'post' | 'sync'
}
// ...
// 繼承的 debugger 選項類型
export declare interface DebuggerOptions {
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
// ...options 是一個對象的形式傳入,有以下幾個選項:

其中 onTrack 和 onTrigger 的 e 是 debugger 事件,建議在回調(diào)內(nèi)放置一個 debugger 語句 以調(diào)試依賴,這兩個選項僅在開發(fā)模式下生效。
TIP
deep 默認是 false ,
但是在偵聽 reactive 對象或數(shù)組時,會默認為 true ,
詳見 偵聽選項之 deep。
偵聽選項之 deep
deep 選項接受一個布爾值,可以設(shè)置為 true 開啟深度偵聽,或者是 false 關(guān)閉深度偵聽,默認情況下這個選項是 false 關(guān)閉深度偵聽的,但也存在特例。
設(shè)置為 false 的情況下,如果直接偵聽一個響應(yīng)式的 引用類型 數(shù)據(jù)(e.g. Object 、 Array … ),雖然它的屬性的值有變化,但對其本身來說是不變的,所以不會觸發(fā) watch 的 callback 。
下面是一個關(guān)閉了深度偵聽的例子:
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
// 定義一個響應(yīng)式數(shù)據(jù),注意用的是 ref 來定義
const nums = ref<number[]>([])
// 2s后給這個數(shù)組添加項目
setTimeout(() => {
nums.value.push(1)
// 可以打印一下,確保數(shù)據(jù)確實變化了
console.log('修改后', nums.value)
}, 2000)
// 但是這個 watch 不會按預(yù)期執(zhí)行
watch(
nums,
// 這里的 callback 不會被觸發(fā)
() => {
console.log('觸發(fā)偵聽', nums.value)
},
// 因為關(guān)閉了 deep
{
deep: false,
}
)
},
})
類似這種情況,需要把 deep 設(shè)置為 true 才可以觸發(fā)偵聽。
可以看到上面的例子特地用了 ref API ,這是因為通過 reactive API 定義的對象無法將 deep 成功設(shè)置為 false (這一點在目前的官網(wǎng)文檔未找到說明,最終是在 watch API 的源碼 上找到了答案)。
// ...
if (isReactive(source)) {
getter = () => source
deep = true // 被強制開啟了
}
// ...
這個情況就是上面所說的 “特例” ,可以通過 isReactive API 來判斷是否需要手動開啟深度偵聽。
// 導(dǎo)入 isReactive API
import { defineComponent, isReactive, reactive, ref } from 'vue'
export default defineComponent({
setup() {
// 偵聽這個數(shù)據(jù)時,會默認開啟深度偵聽
const foo = reactive({
name: 'Petter',
age: 18,
})
console.log(isReactive(foo)) // true
// 偵聽這個數(shù)據(jù)時,不會默認開啟深度偵聽
const bar = ref({
name: 'Petter',
age: 18,
})
console.log(isReactive(bar)) // false
},
})
偵聽選項之 immediate
在 偵聽后的回調(diào)函數(shù) 部分有了解過, watch 默認是惰性的,也就是只有當被偵聽的數(shù)據(jù)源發(fā)生變化時才執(zhí)行回調(diào)。
這句話是什么意思呢?來看一下這段代碼,為了減少 deep 選項的干擾,換一個類型,換成 string 數(shù)據(jù)來演示,請留意注釋:
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
// 這個時候不會觸發(fā) watch 的回調(diào)
const message = ref<string>('')
// 2s后改變數(shù)據(jù)
setTimeout(() => {
// 來到這里才會觸發(fā) watch 的回調(diào)
message.value = 'Hello World!'
}, 2000)
watch(message, () => {
console.log('觸發(fā)偵聽', message.value)
})
},
})
可以看到,數(shù)據(jù)在初始化的時候并不會觸發(fā)偵聽回調(diào),如果有需要的話,通過 immediate 選項來讓它直接觸發(fā)。
immediate 選項接受一個布爾值,默認是 false ,可以設(shè)置為 true 讓回調(diào)立即執(zhí)行。
改成這樣,請留意高亮的代碼部分和新的注釋:
import { defineComponent, ref, watch } from 'vue'
export default defineComponent({
setup() {
// 這一次在這里可以會觸發(fā) watch 的回調(diào)了
const message = ref<string>('')
// 2s后改變數(shù)據(jù)
setTimeout(() => {
// 這一次,這里是第二次觸發(fā) watch 的回調(diào),不再是第一次
message.value = 'Hello World!'
}, 2000)
watch(
message,
() => {
console.log('觸發(fā)偵聽', message.value)
},
// 設(shè)置 immediate 選項
{
immediate: true,
}
)
},
})
注意,在帶有 immediate 選項時,不能在第一次回調(diào)時取消該數(shù)據(jù)源的偵聽,詳見 停止偵聽 部分。
偵聽選項之 flush
flush 選項是用來控制 偵聽回調(diào) 的調(diào)用時機,接受指定的字符串,可選值如下,默認是 ‘pre’ 。

對于 ‘pre’ 和 ‘post’ ,回調(diào)使用隊列進行緩沖?;卣{(diào)只被添加到隊列中一次。
即使觀察值變化了多次,值的中間變化將被跳過,不會傳遞給回調(diào),這樣做不僅可以提高性能,還有助于保證數(shù)據(jù)的一致性。
更多關(guān)于 flush 的信息,請參閱 回調(diào)的觸發(fā)時機 。
停止偵聽
如果在 setup 或者 script-setup 里使用 watch 的話, 組件被卸載 的時候也會一起被停止,一般情況下不太需要關(guān)心如何停止偵聽。
不過有時候可能想要手動取消, Vue 3 也提供了方法。
TIP
隨著組件被卸載一起停止的前提是,偵聽器必須是 同步語句 創(chuàng)建的,
這種情況下偵聽器會綁定在當前組件上。如果放在 setTimeout 等 異步函數(shù) 里面創(chuàng)建,
則不會綁定到當前組件,因此組件卸載的時候不會一起停止該偵聽器,
這種時候就需要手動停止偵聽。
在 API 的 TS 類型 有提到,當在定義一個 watch 行為的時候,它會返回一個用來停止偵聽的函數(shù)。
這個函數(shù)的 TS 類型如下:
export declare type WatchStopHandle = () => void
用法很簡單,做一下簡單了解即可:
// 定義一個取消觀察的變量,它是一個函數(shù)
const unwatch = watch(message, () => {
// ...
})
// 在合適的時期調(diào)用它,可以取消這個偵聽
unwatch()但是也有一點需要注意的是,如果啟用了 immediate 選項 ,不能在第一次觸發(fā)偵聽回調(diào)時執(zhí)行它。
// 注意:這是一段錯誤的代碼,運行會報錯
const unwatch = watch(
message,
// 偵聽的回調(diào)
() => {
// ...
// 在這里調(diào)用會有問題 ?
unwatch()
},
// 啟用 immediate 選項
{
immediate: true,
}
)會收獲一段報錯,告訴 unwatch 這個變量在初始化前無法被訪問:
Uncaught ReferenceError: Cannot access 'unwatch' before initialization
目前有兩種方案可以讓實現(xiàn)這個操作:
方案一:使用 var 并判斷變量類型,利用 var 的變量提升 來實現(xiàn)目的。
// 這里改成 var ,不要用 const 或 let
var unwatch = watch(
message,
// 偵聽回調(diào)
() => {
// 這里加一個判斷,是函數(shù)才執(zhí)行它
if (typeof unwatch === 'function') {
unwatch()
}
},
// 偵聽選項
{
immediate: true,
}
)不過 var 已經(jīng)屬于過時的語句了,建議用方案二的 let 。
方案二:使用 let 并判斷變量類型。
// 如果不想用 any ,可以導(dǎo)入 TS 類型
import type { WatchStopHandle } from 'vue'
// 這里改成 let ,但是要另起一行,先定義,再賦值
let unwatch: WatchStopHandle
unwatch = watch(
message,
// 偵聽回調(diào)
() => {
// 這里加一個判斷,是函數(shù)才執(zhí)行它
if (typeof unwatch === 'function') {
unwatch()
}
},
// 偵聽選項
{
immediate: true,
}
)偵聽效果清理
在 偵聽后的回調(diào)函數(shù) 部分提及到一個參數(shù) onCleanup ,它可以幫注冊一個清理函數(shù)。
有時 watch 的回調(diào)會執(zhí)行異步操作,當 watch 到數(shù)據(jù)變更的時候,需要取消這些操作,這個函數(shù)的作用就用于此,會在以下情況調(diào)用這個清理函數(shù):
watcher 即將重新運行的時候
watcher 被停止(組件被卸載或者被手動 停止偵聽 )
TS 類型:
declare type OnCleanup = (cleanupFn: () => void) => void
用法方面比較簡單,傳入一個回調(diào)函數(shù)運行即可,不過需要注意的是,需要在停止偵聽之前注冊好清理行為,否則不會生效。
在 停止偵聽 里的最后一個 immediate 例子的基礎(chǔ)上繼續(xù)添加代碼,請注意注冊的時機:
let unwatch: WatchStopHandle
unwatch = watch(
message,
(newValue, oldValue, onCleanup) => {
// 需要在停止偵聽之前注冊好清理行為
onCleanup(() => {
console.log('偵聽清理ing')
// 根據(jù)實際的業(yè)務(wù)情況定義一些清理操作 ...
})
// 然后再停止偵聽
if (typeof unwatch === 'function') {
unwatch()
}
},
{
immediate: true,
}
)watchEffect
如果一個函數(shù)里包含了多個需要偵聽的數(shù)據(jù),一個一個數(shù)據(jù)去偵聽太麻煩了,在 Vue 3 ,可以直接使用 watchEffect API 來簡化的操作。
API 的 TS 類型
這個 API 的類型如下,使用的時候需要傳入一個副作用函數(shù)(相當于 watch 的 偵聽后的回調(diào)函數(shù) ),也可以根據(jù)的實際情況傳入一些可選的 偵聽選項 。
和 watch API 一樣,它也會返回一個用于 停止偵聽 的函數(shù)。
// watchEffect 部分的 TS 類型 // ... export declare type WatchEffect = (onCleanup: OnCleanup) => void export declare function watchEffect( effect: WatchEffect, options?: WatchOptionsBase ): WatchStopHandle // ...
副作用函數(shù)也會傳入一個清理回調(diào)作為參數(shù),和 watch 的 偵聽效果清理 一樣的用法。
可以理解為它是一個簡化版的 watch ,具體簡化在哪里呢?請看下面的用法示例。
用法示例
它立即執(zhí)行傳入的一個函數(shù),同時響應(yīng)式追蹤其依賴,并在其依賴變更時重新運行該函數(shù)。
import { defineComponent, ref, watchEffect } from 'vue'
export default defineComponent({
setup() {
// 單獨定義兩個數(shù)據(jù),后面用來分開改變數(shù)值
const name = ref<string>('Petter')
const age = ref<number>(18)
// 定義一個調(diào)用這兩個數(shù)據(jù)的函數(shù)
const getUserInfo = (): void => {
console.log({
name: name.value,
age: age.value,
})
}
// 2s后改變第一個數(shù)據(jù)
setTimeout(() => {
name.value = 'Tom'
}, 2000)
// 4s后改變第二個數(shù)據(jù)
setTimeout(() => {
age.value = 20
}, 4000)
// 直接偵聽調(diào)用函數(shù),在每個數(shù)據(jù)產(chǎn)生變化的時候,它都會自動執(zhí)行
watchEffect(getUserInfo)
},
})和 watch 的區(qū)別
雖然理論上 watchEffect 是 watch 的一個簡化操作,可以用來代替 批量偵聽 ,但它們也有一定的區(qū)別:
1.watch 可以訪問偵聽狀態(tài)變化前后的值,而 watchEffect 沒有。
2.watch 是在屬性改變的時候才執(zhí)行,而 watchEffect 則默認會執(zhí)行一次,然后在屬性改變的時候也會執(zhí)行。
第二點的意思,看下面這段代碼可以有更直觀的理解:
使用 watch :
export default defineComponent({
setup() {
const foo = ref<string>('')
setTimeout(() => {
foo.value = 'Hello World!'
}, 2000)
function bar() {
console.log(foo.value)
}
// 使用 watch 需要先手動執(zhí)行一次
bar()
// 然后當 foo 有變動時,才會通過 watch 來執(zhí)行 bar()
watch(foo, bar)
},
})使用 watchEffect :
export default defineComponent({
setup() {
const foo = ref<string>('')
setTimeout(() => {
foo.value = 'Hello World!'
}, 2000)
function bar() {
console.log(foo.value)
}
// 可以通過 watchEffect 實現(xiàn) bar() + watch(foo, bar) 的效果
watchEffect(bar)
},
})可用的偵聽選項
雖然用法和 watch 類似,但也簡化了一些選項,它的偵聽選項 TS 類型如下:
// 只支持 base 類型
export declare interface WatchOptionsBase extends DebuggerOptions {
flush?: 'pre' | 'post' | 'sync'
}
// ...
// 繼承的 debugger 選項類型
export declare interface DebuggerOptions {
onTrack?: (event: DebuggerEvent) => void
onTrigger?: (event: DebuggerEvent) => void
}
// ...對比 watch API ,它不支持 deep 和 immediate ,請記住這一點,其他的用法是一樣的。
flush 選項的使用詳見 偵聽選項之 flush ,onTrack 和 onTrigger 詳見 偵聽的選項 部分內(nèi)容。
watchPostEffect
watchEffect API 使用 flush: ‘post’ 選項時的別名,具體區(qū)別詳見 偵聽選項之 flush 部分。
TIP
Vue v3.2.0 及以上版本才支持該 API 。
watchSyncEffect
watchEffect API 使用 flush: ‘sync’ 選項時的別名,具體區(qū)別詳見 偵聽選項之 flush 部分。
TIP
Vue v3.2.0 及以上版本才支持該 API 。
總結(jié)
到此這篇關(guān)于VUE3數(shù)據(jù)的偵聽的文章就介紹到這了,更多相關(guān)VUE3數(shù)據(jù)偵聽內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue使用JSEncrypt對密碼本地存儲時加解密的實現(xiàn)
本文主要介紹了vue使用JSEncrypt對密碼本地存儲時加解密,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2023-07-07
Vue 父子組件實現(xiàn)數(shù)據(jù)雙向綁定效果的兩種方式(案例代碼)
本文給大家分享Vue 父子組件實現(xiàn)數(shù)據(jù)雙向綁定效果的兩種方式,方式一是通過監(jiān)聽事件實現(xiàn)方式二是通過 v-model 實現(xiàn),每種方式結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2022-11-11
Vue3生命周期Hooks原理與調(diào)度器Scheduler關(guān)系
這篇文章主要為大家介紹了Vue3生命周期Hooks原理與調(diào)度器Scheduler關(guān)系詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
vue中如何實現(xiàn)后臺管理系統(tǒng)的權(quán)限控制的方法步驟
這篇文章主要介紹了vue中如何實現(xiàn)后臺管理系統(tǒng)的權(quán)限控制的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2019-09-09
詳解vue中使用transition和animation的實例代碼
這篇文章主要介紹了詳解vue中使用transition和animation的實例代碼,本文給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
vue使用assign巧妙重置data數(shù)據(jù)方式
這篇文章主要介紹了vue使用assign巧妙重置data數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Vue中避免濫用this去讀取data中數(shù)據(jù)
這篇文章主要介紹了Vue中避免濫用this去讀取data中數(shù)據(jù)的的相關(guān)資料,幫助大家更好的理解和學(xué)習使用vue框架,感興趣的朋友可以了解下2021-03-03

