p in props"/> p in props">

Home  >  Article  >  Web Front-end  >  How to use watch to monitor the property value of an object in Vue3

How to use watch to monitor the property value of an object in Vue3

WBOY
WBOYforward
2023-06-03 13:09:345178browse

Use watch in Vue3 to listen to specific properties in objects

1. Preface

<script lang="ts" setup>
	// 接受父组件传递的数据
    const props = defineProps({
        test: {
            type: String,
            default: &#39;&#39;
        }
    })
    
    // 使用 watch 侦听 props 中的 test 属性
    watch(
        // 这种写法不会侦听到 props 中 test 的变化
    	props.test,
        () => {
            console.log("侦听成功")
        }
    )
    
    watch(
    	// 这种写法会侦听到 props 中 test 的变化
        () => props.test,
        () => {
            console.log("侦听成功")
        }
    )
</script>

Basic usage of watch

watch () The default is lazy listening, that is, the callback function is only executed when the listening source changes

The first parameter: listening source, listening The listening source can be one of the following types

A function that returns a value, a ref, a responsive object (reactive) or an array composed of the above types of values

The second parameter: when the listening source changes The callback function that is triggered.

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

When listening to multiple sources, the callback function accepts two arrays, corresponding to the new values ​​​​in the source array. and old values

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

The third parameter: optional object, can be supported These options

immediate: Trigger the callback immediately when the listener is created deep: If the source is an object, depth traversal is forced so that the callback function is triggered when the deep level changes flush: Adjust the refresh timing of the callback function onTrack/onTrigger: Dependencies of the debugging listener

2. Reason

Because the listening source of watch can only be the 4 situations above

const obj = reactive({ count: 0 })

// 错误,因为 watch() 中的侦听源是一个 number,最终 source 返回的 getter 函数是一个空,所以就得不到侦听的数据
watch(obj.count, (count) => {
  console.log(`count is: ${count}`)
})

// 正确,主要思想是,将侦听源转化为以上4种类型(转化为getter函数是最简单方便的)
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)

3. watch source code analysis

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)
}

It can be seen from the source code that watch receives three parameters: sourcelistening source, cbcallback function, optionsListening configuration will eventually return a doWatch

4.doWatch source code analysis

function doWatch(
  source: WatchSource | WatchSource[] | WatchEffect | object,
  cb: WatchCallback | null,
  { immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
  // ...
// 当前组件实例
const instance = currentInstance
// 副作用函数,在初始化effect时使用
let getter: () => any
// 强制触发侦听
let forceTrigger = false
// 是否为多数据源。
let isMultiSource = false
}

doWatchstill accepts three Parameters: sourceListening source, cbCallback function, optionsListening configuration

Here we focus on analyzing the source code of the listening source (source standardization)

  • If source is a ref type, getter is a return# The function of ##source.value, forceTrigger depends on whether source is shallow responsive.

  • if (isRef(source)) {
      getter = () => source.value
      forceTrigger = isShallow(source)
    }
  • If

    source is of reactive type, getter is a return source function and set deep to true. When listening directly to a reactive object, the listener automatically enables deep mode

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

<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 中的任一属性变化了,都会被监听到
watch(obj, () => {
  console.log("变化了");
});
</script>

  • if

    source is an array, set isMultiSource to true, forceTrigger depends on whether source has a reactive type The data in the getter function will traverse the source and do different processing for different types of 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)
          }
        })
    }
  • If

    source is a function. When cb exists, source will be executed in the getter function, where source will be executed through the callWithErrorHandling function , errors that occur during the execution of source will be handled in callWithErrorHandling; if cb does not exist, in getter, if the component has Has been uninstalled, directly return, otherwise judge cleanup (cleanup is registered through onCleanup in watchEffect Cleanup function), if cleanup exists, execute cleanup, then execute source, and return the execution result. source will be wrapped by callWithAsyncErrorHandling. This function will handle errors that occur during the execution of source. The difference from callWithErrorHandling is that callWithAsyncErrorHandling will handle asynchronous errors.

  • if (isFunction(source)) {
      if (cb) {
        getter = () =>
          callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
      } else {
        // watchEffect
        getter = () => {
          // 如果组件实例已经卸载,直接return
          if (instance && instance.isUnmounted) {
            return
          }
          // 如果清理函数,则执行清理函数
          if (cleanup) {
            cleanup()
          }
          // 执行source,传入onCleanup,用来注册清理函数
          return callWithAsyncErrorHandling(
            source,
            instance,
            ErrorCodes.WATCH_CALLBACK,
            [onCleanup]
          )
        }
      }
    }
  • Other cases

    getter will be assigned to an empty function

  • getter = NOOP
    __DEV__ && warnInvalidSource(source)

The above is the detailed content of How to use watch to monitor the property value of an object in Vue3. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete