vue3中watch和watchEffect的區(qū)別
watch和watchEffect
watchEffect()
官網(wǎng)介紹:立即運行一個函數(shù),同時響應(yīng)式地追蹤其依賴,并在依賴更改時重新執(zhí)行。
類型
function watchEffect( effect: (onCleanup: OnCleanup) => void, options?: WatchEffectOptions ): StopHandle ? type OnCleanup = (cleanupFn: () => void) => void ? interface WatchEffectOptions { flush?: 'pre' | 'post' | 'sync' // 默認:'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void } ? type StopHandle = () => void
詳細信息
- 第一個參數(shù)就是要運行的副作用函數(shù)。這個副作用函數(shù)的參數(shù)也是一個函數(shù),用來注冊清理回調(diào)。清理回調(diào)會在該副作用下一次執(zhí)行前被調(diào)用,可以用來清理無效的副作用,例如等待中的異步請求 (參見下面的示例)。
- 第二個參數(shù)是一個可選的選項,可以用來調(diào)整副作用的刷新時機或調(diào)試副作用的依賴。
默認情況下,偵聽器將在組件渲染之前執(zhí)行。設(shè)置 flush: 'post' 將會使偵聽器延遲到組件渲染之后再執(zhí)行。詳見回調(diào)的觸發(fā)時機。在某些特殊情況下 (例如要使緩存失效),可能有必要在響應(yīng)式依賴發(fā)生改變時立即觸發(fā)偵聽器。這可以通過設(shè)置 flush: 'sync' 來實現(xiàn)。然而,該設(shè)置應(yīng)謹慎使用,因為如果有多個屬性同時更新,這將導致一些性能和數(shù)據(jù)一致性的問題。
返回值是一個用來停止該副作用的函數(shù)。
示例
const count = ref(0) watchEffect(() => console.log(count.value)) // -> 輸出 0 count.value++ // -> 輸出 1
副作用清除:
watchEffect(async (onCleanup) => { const { response, cancel } = doAsyncWork(id.value) // `cancel` 會在 `id` 更改時調(diào)用 // 以便取消之前 // 未完成的請求 onCleanup(cancel) data.value = await response })
停止偵聽器:
const stop = watchEffect(() => {}) // 當不再需要此偵聽器時: stop()
選項:
watchEffect(() => {}, { flush: 'post' })
功能及其使用官網(wǎng)寫的清楚,再來了解下它的原理
function watchEffect( source: WatchEffect, options?: WatchOptionsBase ): WatchStopHandle { let getter = () => { if (cleanup) { cleanup() } return source(onCleanup) } let cleanup: () => void let onCleanup: OnCleanup = (fn: () => void) => { cleanup = effect.onStop = () => fn() } const job: SchedulerJob = () => { if (!effect.active) { return } effect.run() } let scheduler: EffectScheduler if (flush === 'sync') { scheduler = job as any } else if (flush === 'post') { scheduler = () => queuePostRenderEffect(job) } else { // default: 'pre' scheduler = () => queuePreFlushCb(job) } // 創(chuàng)建 effect const effect = new ReactiveEffect(getter, scheduler) if (flush === 'post') { queuePostRenderEffect( effect.run.bind(effect) ) } else { effect.run() } return () => { effect.stop() } }
watchEffect的回調(diào)函數(shù)就是一個副作用函數(shù),因為我們使用watchEffect就是偵聽到依賴的變化后執(zhí)行某些操作。什么是副作用(side effect),簡單的說副作用就是執(zhí)行某種操作,如對外部可變數(shù)據(jù)或變量的修改,外部接口的調(diào)用等。
watchEffect內(nèi)部是創(chuàng)建了一個ReactiveEffect對象,接收兩個參數(shù),第一個getter,在執(zhí)行effect.run()時會調(diào)用,第二個scheduler在依賴變化后執(zhí)行。
那我們執(zhí)行watchEffect(() => console.log(count))時,具體做了什么呢,首先創(chuàng)建了個getter,執(zhí)行g(shù)etter做了兩件事,第一個會看有cleanup沒,cleanup會在調(diào)用OnCleanup賦值, 有就執(zhí)行,第二個執(zhí)行source,為watchEffect傳遞的回調(diào)函數(shù),參數(shù)為OnCleanup;然后會創(chuàng)建OnCleanup,這個會在執(zhí)行source時當參數(shù)傳入,當在source里執(zhí)行這個參數(shù),會創(chuàng)建cleanup,這個cleanup的執(zhí)行時機是下一次執(zhí)行前被調(diào)用或停止該副作用時調(diào)用;然后創(chuàng)建scheduler,scheduler主要就是執(zhí)行effect.run()也就是getter,會根據(jù)傳的flush的不同會創(chuàng)建三種執(zhí)行時機不同的scheduler 第一種sync scheduler:為異步執(zhí)行,會在依賴改變時立即執(zhí)行,比如for循環(huán)改變10次依賴就會執(zhí)行10次 第二種 post scheduler:會在組件渲染后執(zhí)行 第三種 pre scheduler:默認,會在組件渲染前執(zhí)行 然后會創(chuàng)建ReactiveEffect,參數(shù)為之前創(chuàng)建getter和scheduler,創(chuàng)建完后接著會根據(jù)傳的flush來執(zhí)行effect.run(),是post會延遲在組件渲染后執(zhí)行,否則就立即執(zhí)行,執(zhí)行effect.run()會觸發(fā)依賴手收集, 最后返回停止該副作用的函數(shù),執(zhí)行會執(zhí)行effect.stop(),這個會觸發(fā)cleanup
wath()
官網(wǎng)介紹:偵聽一個或多個響應(yīng)式數(shù)據(jù)源,并在數(shù)據(jù)源變化時調(diào)用所給的回調(diào)函數(shù)。
類型
// 偵聽單個來源 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 // 響應(yīng)式對象 interface WatchOptions extends WatchEffectOptions { immediate?: boolean // 默認:false deep?: boolean // 默認:false flush?: 'pre' | 'post' | 'sync' // 默認:'pre' onTrack?: (event: DebuggerEvent) => void onTrigger?: (event: DebuggerEvent) => void }
詳細信息
watch() 默認是懶偵聽的,即僅在偵聽源發(fā)生變化時才執(zhí)行回調(diào)函數(shù)。
第一個參數(shù)是偵聽器的源。這個來源可以是以下幾種:
- 一個函數(shù),返回一個值
- 一個 ref
- 一個響應(yīng)式對象
- ...或是由以上類型的值組成的數(shù)組
第二個參數(shù)是在發(fā)生變化時要調(diào)用的回調(diào)函數(shù)。這個回調(diào)函數(shù)接受三個參數(shù):新值、舊值,以及一個用于注冊副作用清理的回調(diào)函數(shù)。該回調(diào)函數(shù)會在副作用下一次重新執(zhí)行前調(diào)用,可以用來清除無效的副作用,例如等待中的異步請求。
當偵聽多個來源時,回調(diào)函數(shù)接受兩個數(shù)組,分別對應(yīng)來源數(shù)組中的新值和舊值。
第三個可選的參數(shù)是一個對象,支持以下這些選項:
- immediate:在偵聽器創(chuàng)建時立即觸發(fā)回調(diào)。第一次調(diào)用時舊值是 undefined。
- deep:如果源是對象,強制深度遍歷,以便在深層級變更時觸發(fā)回調(diào)。參考深層偵聽器。
- flush:調(diào)整回調(diào)函數(shù)的刷新時機。參考回調(diào)的刷新時機及 watchEffect()。
- onTrack / onTrigger:調(diào)試偵聽器的依賴。參考調(diào)試偵聽器。
與 watchEffect() 相比,watch() 使我們可以:
- 懶執(zhí)行副作用;
- 更加明確是應(yīng)該由哪個狀態(tài)觸發(fā)偵聽器重新執(zhí)行;
- 可以訪問所偵聽狀態(tài)的前一個值和當前值。
示例
偵聽一個 getter 函數(shù):
const state = reactive({ count: 0 }) watch( () => state.count, (count, prevCount) => { /* ... */ } )
偵聽一個 ref:
const count = ref(0) watch(count, (count, prevCount) => { /* ... */ })
當偵聽多個來源時,回調(diào)函數(shù)接受兩個數(shù)組,分別對應(yīng)來源數(shù)組中的新值和舊值:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })
當使用 getter 函數(shù)作為源時,回調(diào)只在此函數(shù)的返回值變化時才會觸發(fā)。如果你想讓回調(diào)在深層級變更時也能觸發(fā),你需要使用 { deep: true } 強制偵聽器進入深層級模式。在深層級模式時,如果回調(diào)函數(shù)由于深層級的變更而被觸發(fā),那么新值和舊值將是同一個對象。
const state = reactive({ count: 0 }) watch( () => state, (newValue, oldValue) => { // newValue === oldValue }, { deep: true } )
當直接偵聽一個響應(yīng)式對象時,偵聽器會自動啟用深層模式:
const state = reactive({ count: 0 }) watch(state, () => { /* 深層級變更狀態(tài)所觸發(fā)的回調(diào) */ })
watch的功能是完全包含watchEffect的,原理其實是差不多的,主要就是getter和scheduler不同
function watch( source: WatchSource | WatchSource[] | WatchEffect | object, cb: WatchCallback | null, { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ ): WatchStopHandle { let getter: () => any if (isRef(source)) { getter = () => source.value } else if (isReactive(source)) { getter = () => source deep = true } else if (isArray(source)) { getter = () => source.map(s => { if (isRef(s)) { return s.value } else if (isReactive(s)) { return traverse(s) } else if (isFunction(s)) { return s() } }) } else if (isFunction(source)) { getter = () => source() } if (cb && deep) { const baseGetter = getter getter = () => traverse(baseGetter()) } let cleanup: () => void let onCleanup: OnCleanup = (fn: () => void) => { cleanup = effect.onStop = () => { fn() } } let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE const job: SchedulerJob = () => { if (!effect.active) { return } // watch(source, cb) const newValue = effect.run() if (cleanup) { cleanup() } cb(newValue, oldValue, onCleanup) oldValue = newValue } let scheduler: EffectScheduler if (flush === 'sync') { scheduler = job as any } else if (flush === 'post') { scheduler = () => queuePostRenderEffect(job) } else { // default: 'pre' scheduler = () => queuePreFlushCb(job) } const effect = new ReactiveEffect(getter, scheduler) if (immediate) { job() } else { oldValue = effect.run() } ? return () => { effect.stop() } }
首先是getter,可以看到會根據(jù)不同source的類型定義不同的getter,包括Ref、Reactive、Array和function四種,getter就是獲取這些類型返回的值,從而觸發(fā)依賴收集;然后是deep,如果deep為true,會改變getter為() => traverse(baseGetter()),traverse()會遞歸獲取里面的值;onCleanup和watchEffect的一樣;scheduler的執(zhí)行時機和watchEffect一樣,主要就是job不一樣,job主要做兩件事,第一個是執(zhí)行cleanup和watchEffect一樣,第二個是執(zhí)行watch(source, cb)的第二個參數(shù)cb,參數(shù)為newValue和oldValue,newValue為effect.run()也就是getter()的返回值,oldValue為上次執(zhí)行的newValue;同樣也創(chuàng)建了ReactiveEffect對象;然后是immediate,為true執(zhí)行job,job里會執(zhí)行effect.run()收集依賴和cb,否則就執(zhí)行effect.run()收集依賴了;最后watch的返回值和watchEffect一樣。
總的來說watch和watchEffect功能很強大,源碼看起來也不是很難,看完源碼使用起來就更加得心應(yīng)手了。
本文主要記錄看watch和watchEffect源碼的一些筆記,沒有涉及響應(yīng)式的收集依賴和觸發(fā)。
到此這篇關(guān)于vue3中watch和watchEffect的區(qū)別的文章就介紹到這了,更多相關(guān)vue3 watch watchEffect內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解Vue3?中的watchEffect?特性
- vue3數(shù)據(jù)監(jiān)聽watch/watchEffect的示例代碼
- vue3中watch與watchEffect的區(qū)別
- vue3中watch和watchEffect實戰(zhàn)梳理
- Vue3中的?computed,watch,watchEffect的使用方法
- Vue3?中?watch?與?watchEffect?區(qū)別及用法小結(jié)
- vue3中的watch和watchEffect實例詳解
- 淺談Vue3中watchEffect的具體用法
- vue3的watch和watchEffect你了解嗎
- VUE3中watch和watchEffect的用法詳解
- Vue3中watchEffect的用途淺析
相關(guān)文章
VueJs路由跳轉(zhuǎn)——vue-router的使用詳解
本篇文章主要介紹了VueJs路由跳轉(zhuǎn)——vue-router的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01vue Nprogress進度條功能實現(xiàn)常見問題
這篇文章主要介紹了vue Nprogress進度條功能實現(xiàn),NProgress是頁面跳轉(zhuǎn)是出現(xiàn)在瀏覽器頂部的進度條,本文通過實例代碼給大家講解,需要的朋友可以參考下2021-07-07vue+mousemove實現(xiàn)鼠標拖動功能(拖動過快失效問題解決方法)
這篇文章主要介紹了vue+mousemove實現(xiàn)鼠標拖動功能,文中給大家介紹了鼠標移動過快拖動就失效問題的解決方法,需要的朋友可以參考下2018-08-08vue3實戰(zhàn)教程之a(chǎn)xios的封裝和環(huán)境變量
這篇文章主要給大家介紹了關(guān)于vue3實戰(zhàn)教程之a(chǎn)xios的封裝和環(huán)境變量的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2022-02-02解決vue偵聽器watch,調(diào)用this時出現(xiàn)undefined的問題
這篇文章主要介紹了解決vue偵聽器watch,調(diào)用this時出現(xiàn)undefined的問題,具有很好的參考2020-10-10