Vue3中watch與watchEffect使用方法詳解
一、監(jiān)聽器的基本概念
在Vue3中,watch和watchEffect都是用于監(jiān)聽數(shù)據(jù)變化并執(zhí)行副作用的API,但它們的使用方式和適用場景有所不同。
- watch:需要顯式指定監(jiān)聽的數(shù)據(jù)源,惰性執(zhí)行(默認(rèn)數(shù)據(jù)變化時才執(zhí)行),可以獲取新舊值
- watchEffect:自動收集依賴,立即執(zhí)行,無法獲取舊值
二、watch的使用方法
2.1 基礎(chǔ)用法:監(jiān)聽單個ref數(shù)據(jù)
<template>
<div>
<p>計數(shù)器: {{ count }}</p>
<button @click="count++">增加</button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
// 創(chuàng)建響應(yīng)式數(shù)據(jù)
const count = ref(0)
// 監(jiān)聽count變化
// 第一個參數(shù):要監(jiān)聽的數(shù)據(jù)源
// 第二個參數(shù):回調(diào)函數(shù),接收新值和舊值
watch(count, (newValue, oldValue) => {
console.log(`count從${oldValue}變?yōu)?{newValue}`)
})
</script>
2.2 監(jiān)聽多個數(shù)據(jù)源【監(jiān)聽多個數(shù)據(jù),用數(shù)組形式傳入】
import { ref, watch } from 'vue'
const firstName = ref('張')
const lastName = ref('三')
// 監(jiān)聽多個數(shù)據(jù),用數(shù)組形式傳入
watch([firstName, lastName], ([newFirst, newLast], [oldFirst, oldLast]) => {
console.log(`姓名從${oldFirst}${oldLast}變?yōu)?{newFirst}${newLast}`)
})
2.3 監(jiān)聽對象屬性【監(jiān)聽對象的某個屬性,使用getter函數(shù)】
import { reactive, watch } from 'vue'
const user = reactive({
name: '張三',
age: 20
})
// 監(jiān)聽對象的某個屬性,使用getter函數(shù)
watch(
() => user.age, // getter函數(shù),返回要監(jiān)聽的屬性
(newAge, oldAge) => { console.log(`年齡從${oldAge}變?yōu)?{newAge}`)
}
)
2.4 深度監(jiān)聽
當(dāng)監(jiān)聽對象或數(shù)組時,默認(rèn)情況下watch不會監(jiān)聽內(nèi)部屬性變化,需要使用deep: true開啟深度監(jiān)聽:
import { ref, watch } from 'vue'
const user = ref({
name: '張三',
address: {
city: '北京'
}
})
// 深度監(jiān)聽對象內(nèi)部變化
watch(
user,
(newVal, oldVal) => { console.log('用戶信息變化:', newVal)},
{ deep: true } // 開啟深度監(jiān)聽
)
// 修改深層屬性會觸發(fā)監(jiān)聽
user.value.address.city = '上海'
2.5 立即執(zhí)行
默認(rèn)情況下,watch是惰性的,只有數(shù)據(jù)變化時才執(zhí)行。使用immediate: true可以讓它在初始化時立即執(zhí)行:
watch(
count,
(newValue, oldValue) => {
console.log(`count變化: ${newValue}`)
},
{ immediate: true } // 立即執(zhí)行
)
三、watchEffect的使用方法【可以不用指定監(jiān)聽的數(shù)據(jù)】
3.1 基礎(chǔ)用法
watchEffect會自動收集函數(shù)內(nèi)使用的響應(yīng)式數(shù)據(jù)作為依賴:
自動追蹤所有響應(yīng)式依賴, 每次依賴變更都會執(zhí)行此函數(shù)
<template>
<div>
<p>計數(shù)器: {{ count }}</p>
<button @click="count++">增加</button>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue'
const count = ref(0)
// watchEffect會自動收集依賴
// 1. 初始化時立即執(zhí)行一次
// 2. 當(dāng)函數(shù)內(nèi)依賴的響應(yīng)式數(shù)據(jù)變化時重新執(zhí)行
watchEffect( () => {console.log(`count的值是: ${count.value}`)}
)
</script>
3.2 停止監(jiān)聽
watchEffect返回一個停止函數(shù),調(diào)用它可以停止監(jiān)聽:
import { ref, watchEffect } from 'vue'
const count = ref(0)
// 獲取停止函數(shù)
const stop = watchEffect(() => {
console.log(`count: ${count.value}`)
})
// 5秒后停止監(jiān)聽
setTimeout(() => {
stop()
console.log('已停止監(jiān)聽')
}, 5000)
3.3 清理副作用
watchEffect的回調(diào)函數(shù)可以返回一個清理函數(shù),在下次執(zhí)行前或停止監(jiān)聽時調(diào)用:
watchEffect((onInvalidate) => {
// 模擬異步操作
const timer = setTimeout(() => {
console.log('異步操作完成')
}, 1000)
// 清理函數(shù),在下次執(zhí)行前或停止監(jiān)聽時調(diào)用
onInvalidate(() => {
clearTimeout(timer)
console.log('清理定時器')
})
})
四、watch與watchEffect的區(qū)別
| 特性 | watch | watchEffect |
|---|---|---|
| 依賴收集 | 需要顯式指定監(jiān)聽源 | 自動收集函數(shù)內(nèi)的響應(yīng)式依賴 |
| 執(zhí)行時機 | 默認(rèn)惰性執(zhí)行(數(shù)據(jù)變化時) | 立即執(zhí)行,然后響應(yīng)式追蹤 |
| 新舊值 | 可以獲取新值和舊值 | 只能獲取新值 |
| 適用場景 | 需要知道數(shù)據(jù)變化前后的值 | 只需響應(yīng)數(shù)據(jù)變化,不需要舊值 |
| 控制粒度 | 精確控制監(jiān)聽源 | 自動追蹤所有依賴 |
五、高級用法
5.1 監(jiān)聽執(zhí)行時機控制
使用flush選項控制回調(diào)執(zhí)行時機:
watch(
count,
() => {
// DOM更新后執(zhí)行
},
{ flush: 'post' } // 'pre'(默認(rèn)) | 'post' | 'sync'
)
5.2 調(diào)試監(jiān)聽器
使用onTrack和onTrigger選項調(diào)試依賴:
watch(
count,
() => {
console.log('count變化了')
},
{
onTrack(e) {
console.log('依賴被追蹤:', e)
},
onTrigger(e) {
console.log('監(jiān)聽器被觸發(fā):', e)
}
}
)
5.3 暫停與恢復(fù)監(jiān)聽(Vue3.5+)
Vue3.5+版本新增了暫停和恢復(fù)監(jiān)聽的功能:
const { pause, resume } = watch(count, () => {
console.log('count變化了')
})
// 暫停監(jiān)聽
pause()
// 恢復(fù)監(jiān)聽
resume()
六、實戰(zhàn)示例
6.1 表單驗證(使用watch)
import { ref, watch } from 'vue'
const username = ref('')
const errorMessage = ref('')
// 監(jiān)聽用戶名變化進(jìn)行驗證
watch(
username,
(newVal) => {
if (newVal.length < 3) {
errorMessage.value = '用戶名至少3個字符'
} else {
errorMessage.value = ''
}
},
{ immediate: true } // 初始化時驗證
)
6.2 數(shù)據(jù)加載與清理(使用watchEffect)
import { ref, watchEffect } from 'vue'
const userId = ref(1)
const userData = ref(null)
watchEffect(async (onInvalidate) => {
// 加載數(shù)據(jù)
const controller = new AbortController()
const signal = controller.signal
try {
const response = await fetch(`/api/user/${userId.value}`, { signal })
userData.value = await response.json()
} catch (error) {
if (error.name !== 'AbortError') {
console.error('加載失敗:', error)
}
}
// 清理函數(shù),取消上一次請求
onInvalidate(() => {
controller.abort()
})
})
七、總結(jié)
- watch適合需要精細(xì)控制監(jiān)聽源、需要新舊值對比或需要延遲執(zhí)行的場景
- watchEffect適合簡單的副作用處理,自動追蹤依賴,代碼更簡潔
- 優(yōu)先使用watchEffect處理簡單邏輯,使用watch處理復(fù)雜場景
- 注意清理副作用,避免內(nèi)存泄漏
- 組件卸載時,同步創(chuàng)建的監(jiān)聽器會自動停止,異步創(chuàng)建的需要手動停止## 八、深入理解與高級技巧
7.1 watch監(jiān)聽reactive對象的注意事項
當(dāng)監(jiān)聽reactive創(chuàng)建的響應(yīng)式對象時,有一些特殊情況需要注意:
import { reactive, watch } from 'vue'
const user = reactive({
name: '張三',
age: 20
})
// 情況一:直接監(jiān)聽reactive對象
// 此時會自動開啟深度監(jiān)聽,無需設(shè)置deep: true
watch(user, (newVal, oldVal) => {
console.log('用戶信息變化', newVal)
})
// 情況二:監(jiān)聽reactive對象的屬性
// 必須使用getter函數(shù),否則無法觸發(fā)監(jiān)聽
watch(
() => user.age, // 正確寫法
(newAge) => {
console.log('年齡變化:', newAge)
}
)
// 錯誤寫法:直接監(jiān)聽屬性無法觸發(fā)
watch(user.age, () => {
console.log('年齡變化') // 不會執(zhí)行
})
7.2 watchEffect的依賴追蹤細(xì)節(jié)
watchEffect的依賴追蹤是動態(tài)的,只追蹤函數(shù)執(zhí)行過程中實際訪問的響應(yīng)式數(shù)據(jù):
import { ref, watchEffect } from 'vue'
const a = ref(0)
const b = ref(0)
watchEffect(() => {
console.log('watchEffect執(zhí)行')
if (a.value > 5) {
console.log('b的值:', b.value)
}
})
// 初始執(zhí)行: 輸出 "watchEffect執(zhí)行"
a.value = 3 // 執(zhí)行: 輸出 "watchEffect執(zhí)行" (a被訪問)
b.value = 5 // 不執(zhí)行 (b未被訪問)
a.value = 6 // 執(zhí)行: 輸出 "watchEffect執(zhí)行" 和 "b的值: 5" (a和b都被訪問)
b.value = 10 // 執(zhí)行: 輸出 "watchEffect執(zhí)行" 和 "b的值: 10" (b現(xiàn)在被訪問了)
7.3 高級調(diào)試技巧
使用onTrack和onTrigger選項可以幫助我們調(diào)試監(jiān)聽器的行為:
watch(
count,
() => {
console.log('count變化了')
},
{
onTrack(e) {
// 當(dāng)依賴被追蹤時觸發(fā)
console.log(`追蹤到依賴: ${e.target}的${e.key}屬性`)
},
onTrigger(e) {
// 當(dāng)監(jiān)聽器被觸發(fā)時觸發(fā)
console.log(`監(jiān)聽器觸發(fā)原因: ${e.type}`)
}
}
)
7.4 性能優(yōu)化策略
7.4.1 避免不必要的深度監(jiān)聽
深度監(jiān)聽會遞歸遍歷對象的所有屬性,對于大型對象會影響性能:
// 不推薦:深度監(jiān)聽大型對象
watch(largeObject, () => {
// ...
}, { deep: true })
// 推薦:只監(jiān)聽需要的屬性
watch(
() => largeObject.importantProperty,
() => {
// ...
}
)
7.4.2 使用防抖/節(jié)流優(yōu)化頻繁觸發(fā)
對于輸入框等頻繁變化的場景,可以結(jié)合防抖/節(jié)流:
import { ref, watchEffect } from 'vue'
import { debounce } from 'lodash'
const searchInput = ref('')
// 使用防抖優(yōu)化搜索請求
const debouncedSearch = debounce(async (value) => {
const results = await fetch(`/api/search?q=${value}`)
// 處理結(jié)果
}, 300)
watchEffect(() => {
debouncedSearch(searchInput.value)
})
八、常見問題與解決方案
8.1 監(jiān)聽器不觸發(fā)的常見原因
- 監(jiān)聽了非響應(yīng)式數(shù)據(jù)
// 錯誤:監(jiān)聽普通變量
let count = 0
watch(count, () => { /* 不會觸發(fā) */ })
// 正確:監(jiān)聽響應(yīng)式數(shù)據(jù)
const count = ref(0)
watch(count, () => { /* 會觸發(fā) */ })
- 直接修改數(shù)組索引或長度
const list = ref([1, 2, 3]) // 錯誤:直接修改索引 list.value[0] = 100 // 不會觸發(fā)監(jiān)聽 // 正確:使用數(shù)組方法或set list.value.push(4) // 會觸發(fā) list.value.splice(0, 1, 100) // 會觸發(fā)
- 監(jiān)聽對象新增屬性
const user = reactive({ name: '張三' })
// 錯誤:直接添加屬性
user.age = 20 // 默認(rèn)不會觸發(fā)監(jiān)聽
// 正確:使用Vue.set或重新賦值
user.age = 20 // 在Vue3中,reactive對象新增屬性會觸發(fā)監(jiān)聽
// 或?qū)τ趓ef對象
user.value = { ...user.value, age: 20 }
8.2 watch與computed的選擇
| 場景 | 推薦使用 | 原因 |
|---|---|---|
| 數(shù)據(jù)轉(zhuǎn)換/計算 | computed | 緩存結(jié)果,更高效 |
| 數(shù)據(jù)變化時執(zhí)行異步操作 | watch | 適合處理副作用 |
| 需要知道數(shù)據(jù)變化前后的值 | watch | computed無法獲取舊值 |
| 簡單的依賴追蹤 | watchEffect | 代碼更簡潔 |
九、完整實戰(zhàn)案例:搜索功能實現(xiàn)
下面是一個結(jié)合watch、watchEffect和computed的完整搜索功能實現(xiàn):
<template>
<div class="search-container">
<input
v-model="searchQuery"
placeholder="搜索..."
@input="handleInput"
>
<div v-if="isSearching" class="loading">搜索中...</div>
<div v-if="errorMessage" class="error">{{ errorMessage }}</div>
<ul v-else-if="results.length">
<li v-for="result in results" :key="result.id">{{ result.name }}</li>
</ul>
<div v-else-if="!isSearching && searchQuery">無結(jié)果</div>
</div>
</template>
<script setup>
import { ref, watch, watchEffect, computed, onUnmounted } from 'vue'
// 響應(yīng)式數(shù)據(jù)
const searchQuery = ref('')
const results = ref([])
const isSearching = ref(false)
const errorMessage = ref('')
const debouncedQuery = ref('')
// 防抖處理
let timeoutId = null
const handleInput = () => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => {
debouncedQuery.value = searchQuery.value
}, 300)
}
// 使用watch處理搜索邏輯
watch(
debouncedQuery,
async (newQuery) => {
if (!newQuery.trim()) {
results.value = []
return
}
isSearching.value = true
errorMessage.value = ''
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(newQuery)}`)
if (!response.ok) throw new Error('搜索失敗')
results.value = await response.json()
} catch (err) {
errorMessage.value = err.message
} finally {
isSearching.value = false
}
},
{ immediate: false }
)
// 使用watchEffect清理定時器
watchEffect((onInvalidate) => {
// 組件卸載時清理定時器
onInvalidate(() => {
clearTimeout(timeoutId)
})
})
// 清理函數(shù)
onUnmounted(() => {
clearTimeout(timeoutId)
})
</script>
十、總結(jié)與最佳實踐
- 優(yōu)先使用watchEffect:當(dāng)你需要自動追蹤多個依賴,且不需要舊值時
- 使用watch的場景:需要明確監(jiān)聽源、需要舊值、需要延遲執(zhí)行或深度監(jiān)聽
- 性能考量:
- 避免對大型對象使用深度監(jiān)聽
- 對頻繁變化的數(shù)據(jù)使用防抖/節(jié)流
- 及時清理副作用,避免內(nèi)存泄漏
- 調(diào)試技巧:使用onTrack和onTrigger追蹤依賴問題
- 組件卸載:異步創(chuàng)建的監(jiān)聽器需要手動停止
通過合理選擇watch和watchEffect,并遵循最佳實踐,可以寫出更高效、更可維護(hù)的Vue3代碼。理解它們的內(nèi)部工作原理和適用場景,將幫助你在不同的業(yè)務(wù)需求中做出正確的技術(shù)選擇。## 十二、底層原理與實現(xiàn)機制
10.1 響應(yīng)式依賴收集流程
Vue3的監(jiān)聽器基于響應(yīng)式系統(tǒng)工作,其核心流程如下:
1. 當(dāng)watch/watchEffect執(zhí)行時,會創(chuàng)建一個"副作用函數(shù)"(effect)
2. 執(zhí)行副作用函數(shù),期間訪問的響應(yīng)式數(shù)據(jù)會被"追蹤"(track)
3. 響應(yīng)式數(shù)據(jù)變化時,會"觸發(fā)"(trigger)相關(guān)的副作用函數(shù)重新執(zhí)行
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 響應(yīng)式數(shù)據(jù) │────?│ 依賴追蹤器 │────?│ 副作用函數(shù)隊列 │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ ▼
└──────────────────────────────────────────┘
(數(shù)據(jù)變化時觸發(fā)副作用執(zhí)行)
10.2 watch與watchEffect的內(nèi)部差異
// watch的簡化實現(xiàn)
function watch(source, callback, options) {
// 創(chuàng)建getter函數(shù)
const getter = isRef(source)
? () => source.value
: typeof source === 'function'
? source
: () => traverse(source);
// 執(zhí)行副作用
let oldValue = undefined;
const effect = effectFn(() => getter(), {
lazy: true, // 懶執(zhí)行,首次不觸發(fā)
scheduler: () => { // 調(diào)度函數(shù)
const newValue = getter();
callback(newValue, oldValue);
oldValue = newValue;
}
});
// 初始化
if (options.immediate) {
callback(getter(), oldValue);
} else {
oldValue = effect();
}
return () => stop(effect);
}
// watchEffect的簡化實現(xiàn)
function watchEffect(effect, options) {
const _effect = effectFn(effect, {
lazy: false, // 立即執(zhí)行
scheduler: queueJob, // 默認(rèn)調(diào)度器
...options
});
return () => stop(_effect);
}
十一、API參數(shù)全解析
11.1 watch完整參數(shù)說明
// TypeScript類型定義
function watch<T>(
source: WatchSource<T> | WatchSource<T>[],
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle
interface WatchOptions extends WatchEffectOptions {
immediate?: boolean // 是否立即執(zhí)行,默認(rèn)false
deep?: boolean // 是否深度監(jiān)聽,默認(rèn)false
flush?: 'pre' | 'post' | 'sync' // 執(zhí)行時機,默認(rèn)'pre'
once?: boolean // 是否只執(zhí)行一次,Vue3.4+,默認(rèn)false
onTrack?: (event: DebuggerEvent) => void // 依賴追蹤時觸發(fā)
onTrigger?: (event: DebuggerEvent) => void // 觸發(fā)更新時觸發(fā)
}
11.2 flush選項詳解
| 取值 | 執(zhí)行時機 | 適用場景 |
|---|---|---|
| ‘pre’ | 組件更新前執(zhí)行 | 需要訪問更新前的DOM |
| ‘post’ | 組件更新后執(zhí)行 | 需要訪問更新后的DOM |
| ‘sync’ | 同步執(zhí)行 | 需要立即響應(yīng)數(shù)據(jù)變化 |
// 示例:在DOM更新后執(zhí)行
watch(
count,
() => {
// 此時可以獲取到更新后的DOM
console.log('DOM高度:', document.getElementById('content').offsetHeight)
},
{ flush: 'post' }
)
十二、TypeScript高級用法
12.1 類型定義與推斷
import { ref, watch, reactive } from 'vue'
// 1. 監(jiān)聽ref
const count = ref<number>(0)
watch<number>(count, (newVal, oldVal) => {
// newVal和oldVal都會被推斷為number類型
})
// 2. 監(jiān)聽reactive對象
interface User {
name: string
age: number
}
const user = reactive<User>({ name: '張三', age: 20 })
watch(
() => user.age,
(newAge: number, oldAge: number) => {
// 顯式指定類型
}
)
// 3. 監(jiān)聽多個源
watch<[number, string]>(
[count, () => user.name],
([newCount, newName], [oldCount, oldName]) => {
// 類型安全
}
)
12.2 自定義監(jiān)聽器類型
import { ref, watch, WatchSource } from 'vue'
// 定義通用監(jiān)聽器類型
type CustomWatcher<T> = (
source: WatchSource<T>,
callback: (newVal: T, oldVal: T) => void
) => void
// 創(chuàng)建帶類型的監(jiān)聽器
const numberWatcher: CustomWatcher<number> = (source, callback) => {
return watch(source, callback)
}
// 使用
const count = ref(0)
numberWatcher(count, (newVal, oldVal) => {
// 類型安全
})
十三、調(diào)試與問題診斷
13.1 使用Vue DevTools調(diào)試
Vue DevTools提供了專門的監(jiān)聽器調(diào)試面板:
- 查看監(jiān)聽器列表:在"Components"面板中選擇組件,查看"Watchers"部分
- 觸發(fā)時機分析:使用"Timeline"面板記錄監(jiān)聽器觸發(fā)時間線
- 依賴可視化:查看每個監(jiān)聽器依賴的響應(yīng)式數(shù)據(jù)
13.2 常見問題診斷流程
監(jiān)聽器不觸發(fā)時的排查步驟:
1. 確認(rèn)監(jiān)聽源是響應(yīng)式數(shù)據(jù) - 使用isRef/isReactive檢查 - 確認(rèn)不是解構(gòu)后的值 2. 檢查監(jiān)聽路徑是否正確 - 對象屬性需使用getter函數(shù) - 數(shù)組需監(jiān)聽整個數(shù)組或使用正確索引 3. 驗證數(shù)據(jù)確實發(fā)生了變化 - 使用console.log打印數(shù)據(jù) - 確認(rèn)不是引用類型數(shù)據(jù)的內(nèi)部變化 4. 檢查是否需要深度監(jiān)聽 - 對象/數(shù)組內(nèi)部變化需設(shè)置deep: true 5. 檢查是否有條件執(zhí)行問題 - watchEffect中是否有條件語句導(dǎo)致依賴未被訪問
十四、性能優(yōu)化高級技巧
14.1 精確監(jiān)聽避免過度觸發(fā)
// 不好的做法:監(jiān)聽整個對象
watch(
user,
() => {
// 即使無關(guān)屬性變化也會觸發(fā)
},
{ deep: true }
)
// 好的做法:只監(jiān)聽需要的屬性
watch(
() => ({ name: user.name, age: user.age }),
({ name, age }) => {
// 只有這兩個屬性變化才觸發(fā)
}
)
14.2 監(jiān)聽器防抖與節(jié)流
import { ref, watch } from 'vue'
import { debounce, throttle } from 'lodash'
// 防抖示例
const searchInput = ref('')
const debouncedSearch = debounce((value) => {
// 搜索邏輯
}, 300)
watch(searchInput, debouncedSearch)
// 節(jié)流示例
const scrollPosition = ref(0)
const throttledScroll = throttle((position) => {
// 滾動處理邏輯
}, 100)
watch(scrollPosition, throttledScroll)
14.3 大數(shù)據(jù)場景優(yōu)化
對于大型列表或復(fù)雜對象,使用shallowRef和shallowReactive減少響應(yīng)式開銷:
import { shallowRef, watch } from 'vue'
// 大型數(shù)據(jù)使用shallowRef
const largeList = shallowRef([])
// 只監(jiān)聽引用變化,不監(jiān)聽內(nèi)部屬性
watch(largeList, () => {
// 只有當(dāng)整個列表被替換時才觸發(fā)
})
十五、Vue2遷移指南
15.1 watch選項式API與組合式API對比
| Vue2選項式API | Vue3組合式API |
|---|---|
javascript watch: { count(newVal, oldVal) { // 處理邏輯 } } | javascript watch(count, (newVal, oldVal) => { // 處理邏輯 }) |
javascript watch: { 'user.name'(newVal) { // 監(jiān)聽嵌套屬性 } } | javascript watch(() => user.name, (newVal) => { // 監(jiān)聽嵌套屬性 }) |
javascript watch: { user: { handler: () => {}, deep: true } } | javascript watch(user, () => {}, { deep: true }) |
15.2 遷移注意事項
- this綁定:Vue3組合式API中沒有this,直接使用響應(yīng)式變量
- 深度監(jiān)聽:Vue3中reactive對象默認(rèn)深度監(jiān)聽,ref對象需要設(shè)置deep: true
- 數(shù)組監(jiān)聽:Vue3中直接修改數(shù)組索引可以觸發(fā)監(jiān)聽(得益于Proxy)
- **watch方法∗∗:實例方法‘this.watch方法**:實例方法`this.watch方法∗∗:實例方法‘this.watch
替換為watch`函數(shù)
十六、單元測試示例
使用Jest測試監(jiān)聽器行為:
import { ref, watch, watchEffect } from 'vue'
import { mount } from '@vue/test-utils'
describe('watch測試', () => {
it('基本監(jiān)聽功能', async () => {
const count = ref(0)
const mockCallback = jest.fn()
watch(count, mockCallback)
// 初始狀態(tài)不觸發(fā)
expect(mockCallback).not.toHaveBeenCalled()
// 修改值后觸發(fā)
count.value = 1
await Promise.resolve() // 等待微任務(wù)完成
expect(mockCallback).toHaveBeenCalledWith(1, 0)
})
it('watchEffect立即執(zhí)行', () => {
const mockEffect = jest.fn()
watchEffect(mockEffect)
// 立即執(zhí)行
expect(mockEffect).toHaveBeenCalled()
})
})
十七、總結(jié):監(jiān)聽器選擇決策指南
面對不同場景,如何選擇合適的監(jiān)聽器API:
┌─────────────────────────────────────────────┐ │ 需要知道數(shù)據(jù)變化前后的值 │ │ ↓ │ │ 需要明確指定監(jiān)聽源 │ │ ↓ ┌───────────────┐ │ │ 是────────────?│ watch │?─────────┐ │ ↓ └───────────────┘ │ │ 否 │ │ ↓ ┌───────────────┐ │ │ 需要自動收集依賴 ─────────────?│watchEffect│?─────────┐ │ └───────────────┘ │ │ │ │ 需要緩存計算結(jié)果 ─────────────?│ computed │ └─────────────────────────────────────────────┘
最終建議:
- 簡單場景優(yōu)先使用
watchEffect,減少樣板代碼 - 需要精確控制時使用
watch - 純數(shù)據(jù)轉(zhuǎn)換使用
computed - 始終考慮性能影響,避免過度監(jiān)聽
- 復(fù)雜邏輯拆分到組合式函數(shù)中管理監(jiān)聽器
通過這一章節(jié)的補充,您現(xiàn)在應(yīng)該對Vue3的監(jiān)聽器系統(tǒng)有了全面且深入的理解,能夠在各種場景下選擇合適的API并編寫高效、可維護(hù)的代碼。
到此這篇關(guān)于Vue3中watch與watchEffect使用方法的文章就介紹到這了,更多相關(guān)Vue3 watch與watchEffect詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(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中watchEffect和watch的基礎(chǔ)應(yīng)用詳解
相關(guān)文章
vue 插值 v-once,v-text, v-html詳解
這篇文章主要介紹了vue 插值 v-once,v-text, v-html詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
vue項目引入百度地圖BMapGL鼠標(biāo)繪制和BMap輔助工具
這篇文章主要為大家介紹了vue項目引入百度地圖BMapGL鼠標(biāo)繪制和BMap輔助工具的踩坑分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
詳解vue-template-admin三級路由無法緩存的解決方案
這篇文章主要介紹了vue-template-admin三級路由無法緩存的解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03

