溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Vue3中怎么使用watch監(jiān)聽(tīng)對(duì)象的屬性值

發(fā)布時(shí)間:2022-12-28 14:56:01 來(lái)源:億速云 閱讀:207 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“Vue3中怎么使用watch監(jiān)聽(tīng)對(duì)象的屬性值”的相關(guān)知識(shí),小編通過(guò)實(shí)際案例向大家展示操作過(guò)程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Vue3中怎么使用watch監(jiān)聽(tīng)對(duì)象的屬性值”文章能幫助大家解決問(wèn)題。

Vue3 中使用 watch 偵聽(tīng)對(duì)象中的具體屬性

1.前言

<script lang="ts" setup>
	// 接受父組件傳遞的數(shù)據(jù)
    const props = defineProps({
        test: {
            type: String,
            default: ''
        }
    })
    
    // 使用 watch 偵聽(tīng) props 中的 test 屬性
    watch(
        // 這種寫(xiě)法不會(huì)偵聽(tīng)到 props 中 test 的變化
    	props.test,
        () => {
            console.log("偵聽(tīng)成功")
        }
    )
    
    watch(
    	// 這種寫(xiě)法會(huì)偵聽(tīng)到 props 中 test 的變化
        () => props.test,
        () => {
            console.log("偵聽(tīng)成功")
        }
    )
</script>

watch 的基本用法

watch() 默認(rèn)是懶偵聽(tīng)的,即僅在偵聽(tīng)源發(fā)生變化時(shí)才執(zhí)行回調(diào)函數(shù)

第一個(gè)參數(shù):偵聽(tīng)源,偵聽(tīng)源可以是一下幾種

一個(gè)函數(shù),返回一個(gè)值一個(gè) ref一個(gè)響應(yīng)式對(duì)象(reactive)或是由以上類型的值組成的數(shù)組

第二個(gè)參數(shù):偵聽(tīng)源發(fā)生變化時(shí)要觸發(fā)的回調(diào)函數(shù)。

(newValue, oldValue) => { /* code */}

當(dāng)偵聽(tīng)多個(gè)來(lái)源時(shí),回調(diào)函數(shù)接受兩個(gè)數(shù)組,分別對(duì)應(yīng)源數(shù)組中的新值和舊值

( [ newValue1, newValue2 ] , [ oldValue1 , oldValue2 ]) => {/* code */}

第三個(gè)參數(shù):可選對(duì)象,可以支持一下這些選項(xiàng)

immediate:偵聽(tīng)器創(chuàng)建時(shí)立即觸發(fā)回調(diào)deep:如果源是一個(gè)對(duì)象,會(huì)強(qiáng)制深度遍歷,以便在深層級(jí)發(fā)生變化時(shí)觸發(fā)回調(diào)函數(shù)flush:調(diào)整回調(diào)函數(shù)的刷新時(shí)機(jī)onTrack / onTrigger:調(diào)試偵聽(tīng)器的依賴

2. 原因

因?yàn)?code>watch的偵聽(tīng)源只能是上面的4中情況

const obj = reactive({ count: 0 })

// 錯(cuò)誤,因?yàn)?nbsp;watch() 中的偵聽(tīng)源是一個(gè) number,最終 source 返回的 getter 函數(shù)是一個(gè)空,所以就得不到偵聽(tīng)的數(shù)據(jù)
watch(obj.count, (count) => {
  console.log(`count is: ${count}`)
})

// 正確,主要思想是,將偵聽(tīng)源轉(zhuǎn)化為以上4種類型(轉(zhuǎn)化為getter函數(shù)是最簡(jiǎn)單方便的)
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)

3.watch源碼分析

export function watch<T = any, Immediate extends Readonly<boolean> = false>(
  source: T | WatchSource<T>,
  cb: any,
  options?: WatchOptions<Immediate>
): WatchStopHandle {
  if (__DEV__ && !isFunction(cb)) {
    warn(
      `\`watch(fn, options?)\` signature has been moved to a separate API. ` +
      `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
      `supports \`watch(source, cb, options?) signature.`
    )
  }
  return doWatch(source as any, cb, options)
}

從源碼中可以看出,watch接收三個(gè)參數(shù):source偵聽(tīng)源、cb回調(diào)函數(shù)、options偵聽(tīng)配置,最后會(huì)返回一個(gè)doWatch

4.doWatch源碼分析

function doWatch(
  source: WatchSource | WatchSource[] | WatchEffect | object,
  cb: WatchCallback | null,
  { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
  // ...
// 當(dāng)前組件實(shí)例
const instance = currentInstance
// 副作用函數(shù),在初始化effect時(shí)使用
let getter: () => any
// 強(qiáng)制觸發(fā)偵聽(tīng)
let forceTrigger = false
// 是否為多數(shù)據(jù)源。
let isMultiSource = false
}

doWatch依然接受三個(gè)參數(shù):source偵聽(tīng)源、cb回調(diào)函數(shù)、options偵聽(tīng)配置

這里著重對(duì)偵聽(tīng)源的源碼進(jìn)行分析(source標(biāo)準(zhǔn)化

  • 如果sourceref類型,getter是個(gè)返回source.value的函數(shù),forceTrigger取決于source是否是淺層響應(yīng)式。

if (isRef(source)) {
  getter = () => source.value
  forceTrigger = isShallow(source)
}
  • 如果sourcereactive類型,getter是個(gè)返回source的函數(shù),并將deep設(shè)置為true。 當(dāng)直接偵聽(tīng)一個(gè)響應(yīng)式對(duì)象時(shí),偵聽(tīng)器會(huì)自動(dòng)啟用深層模式

if (isReactive(source)) {
  getter = () => source
  deep = true
}

例子

<template>
  <div class="container">
    <h3>obj---{{ obj }}</h3>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年齡</button>
  </div>
</template>

<script lang="ts" setup>
import { reactive, watch } from "vue";
const obj = reactive({
  name: "張三",
  age: 18,
});
const changeName = () => {
  obj.name += "++";
};
const changeAge = () => {
  obj.age += 1;
};
// obj 中的任一屬性變化了,都會(huì)被監(jiān)聽(tīng)到
watch(obj, () => {
  console.log("變化了");
});
</script>
  • 如果source是個(gè)數(shù)組,將isMultiSource設(shè)為true,forceTrigger取決于source是否有reactive類型的數(shù)據(jù),getter函數(shù)中會(huì)遍歷source,針對(duì)不同類型的source做不同處理。

if (isArray(source)) {
  isMultiSource = true
  forceTrigger = source.some(isReactive)
  getter = () =>
    source.map(s => {
      if (isRef(s)) {
        return s.value
      } else if (isReactive(s)) {
        return traverse(s)
      } else if (isFunction(s)) {
        return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
      } else {
        __DEV__ && warnInvalidSource(s)
      }
    })
}
  • 如果source是個(gè)function。存在cb的情況下,getter函數(shù)中會(huì)執(zhí)行source,這里source會(huì)通過(guò)callWithErrorHandling函數(shù)執(zhí)行,在callWithErrorHandling中會(huì)處理source執(zhí)行過(guò)程中出現(xiàn)的錯(cuò)誤;不存在cb的話,在getter中,如果組件已經(jīng)被卸載了,直接return,否則判斷cleanupcleanup是在watchEffect中通過(guò)onCleanup注冊(cè)的清理函數(shù)),如果存在cleanup執(zhí)行cleanup,接著執(zhí)行source,并返回執(zhí)行結(jié)果。source會(huì)被callWithAsyncErrorHandling包裝,該函數(shù)作用會(huì)處理source執(zhí)行過(guò)程中出現(xiàn)的錯(cuò)誤,與callWithErrorHandling不同的是,callWithAsyncErrorHandling會(huì)處理異步錯(cuò)誤。

if (isFunction(source)) {
  if (cb) {
    getter = () =>
      callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
  } else {
    // watchEffect
    getter = () => {
      // 如果組件實(shí)例已經(jīng)卸載,直接return
      if (instance && instance.isUnmounted) {
        return
      }
      // 如果清理函數(shù),則執(zhí)行清理函數(shù)
      if (cleanup) {
        cleanup()
      }
      // 執(zhí)行source,傳入onCleanup,用來(lái)注冊(cè)清理函數(shù)
      return callWithAsyncErrorHandling(
        source,
        instance,
        ErrorCodes.WATCH_CALLBACK,
        [onCleanup]
      )
    }
  }
}
  • 其他情況getter會(huì)被賦值為一個(gè)空函數(shù)

getter = NOOP
__DEV__ && warnInvalidSource(source)

關(guān)于“Vue3中怎么使用watch監(jiān)聽(tīng)對(duì)象的屬性值”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI