vue3.0 watch与watchEffect

watchEffect

在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。

import {
      ref, watchEffect } from 'vue'
export default {
     
	setup() {
     
	    const count = ref(0)
	    setInterval(() => {
     
      		count.value++
    	}, 1000)
    	 /** 这个hook用以监听count.value的变化 */
        watchEffect(() => console.log(count.value))
	    // -> logs 0
	    // -> logs 1
	    // -> logs 2
	    // -> logs 3
	    // -> logs 4
	    // -> logs ...
	    return {
      count }
	}
}

watchEffect里面所有用到的值,其中任何一个发生变化,就会触发watchEffect第一个参数(函数体的执行)

import {
      ref, watchEffect } from 'vue'
export default {
     
	setup() {
     
	    const count = ref(0)
	    const dataCount = ref(0)
	    setInterval(() => {
     
      		count.value++
      		dataCount.value++
    	}, 1000)
    	 /** 这个hook用以监听count.value的变化 */
        watchEffect(() => console.log(count.value,  dataCount.value))
	    // -> logs 0 0
	    // -> logs 1 1
	    // -> logs 2 2
	    // -> logs 3 3
	    // -> logs 4 4
	    // -> logs ...	
	    return {
      count, dataCount  }
	}
}

停止侦听,消除副作用 watchEffect返回的值就是停止侦听的函数。

const stop = watchEffect(() => console.log(count.value))
// setup中,在setup的onUnmounted生命周期停止侦听
onUnmounted(() => {
     
  stop()
})

wtachEffect的类型

function watchEffect(
  effect: (onInvalidate: InvalidateCbRegistrator) => void,
  options?: WatchEffectOptions
): StopHandle

/** 由此可见 WatchEffectOptions 还有可配置选项  */
interface WatchEffectOptions {
     
  flush?: 'pre' | 'post' | 'sync' // 默认:'pre'
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
}

interface DebuggerEvent {
     
  effect: ReactiveEffect
  target: any
  type: OperationTypes
  key: string | symbol | undefined
}

type InvalidateCbRegistrator = (invalidate: () => void) => void

/** 这是watchEffect返回的stop函数,用于消除监听 */
type StopHandle = () => void
import {
      ref, watchEffect, watch, onUnmounted } from 'vue'
export default {
     
  setup() {
     
    const count = ref(0)
    const dataCount = ref(0)

    setInterval(() => {
     
      count.value++
    }, 1000)

    // 停止侦听, watchEffect返回的值就是停止侦听的函数。
    watchEffect(() => console.log(count.value), {
     
      // 将在依赖项变更导致副作用被触发时被调用。
      onTrigger() {
     
        // debugger
        console.log('onTrigger触发了')
      },
      // 将在响应式 property 或 ref 作为依赖项被追踪时被调用。
      onTrack() {
     
        console.log('onTrack触发了')
      },
      // post 在组件更新后触发,这样你就可以访问更新的 DOM。 注意:这也将推迟副作用的初始运行,直到组件的首次渲染完成。
      // 默认 pre
      // flush 选项还接受 sync,这将强制效果始终同步触发。然而,这是低效的,应该很少需要。
      flush: 'pre'
      // onTrack 和 onTrigger 只能在开发模式下工作。
    })
    
    return {
      count }
  }

清除副作用

有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (即完成之前状态已改变了) 。所以侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

副作用即将重新执行时
侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)

watchEffect(onInvalidate => {
     
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
     
    // id has changed or watcher is stopped.
    // invalidate previously pending async operation
    token.cancel()
  })
})

在执行数据请求时,副作用函数往往是一个异步函数:

const data = ref(null)
watchEffect(async onInvalidate => {
     
   onInvalidate(() => {
      /* ... */ }) // 我们在Promise解析之前注册清除函数
  data.value = await fetchData(props.id)
})

总结watchEffect因为依赖变化而重新触发时,上一次watchEffect触发时产生的数据可能还在,或者上一次watchEffect触发的函数可能还在执行,而上一次的数据或者函数,对于业务本身是没有意义的,因为我们肯定是要获取最新的一个状态,执行最新的结果,或者保存最新的数据。

watch

watch API 与选项式 API this.$watch (以及相应的 watch 选项) 完全等效。watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生更改时被调用。

侦听一个源
第一个参数可以是一个具体的变量,也可以是一个函数返回一个变量。

// 侦听一个 getter
const state = reactive({
      count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
     
    /* ... */
  }
)

// 直接侦听一个 ref
const count = ref(0)
watch(count, (count, prevCount) => {
     
  /* ... */
})

侦听多个源

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
     
  /* ... */
})

watch的类型

// 侦听单一源
function watch<T>(
  source: WatcherSource<T>,
  callback: (
    value: T,
    oldValue: T,
    onInvalidate: InvalidateCbRegistrator
  ) => void,
  options?: WatchOptions
): StopHandle

// 侦听多个源
function watch<T extends WatcherSource<unknown>[]>(
  sources: T
  callback: (
    values: MapSources<T>,
    oldValues: MapSources<T>,
    onInvalidate: InvalidateCbRegistrator
  ) => void,
  options? : WatchOptions
): StopHandle

type WatcherSource<T> = Ref<T> | (() => T)

type MapSources<T> = {
     
  [K in keyof T]: T[K] extends WatcherSource<infer V> ? V : never
}

// 参见 `watchEffect` 类型声明共享选项,由此可见vue3.0的watch可配置项与vue2.0的watch可配置项完全一致
interface WatchOptions extends WatchEffectOptions {
     
  immediate?: boolean // 默认:false
  deep?: boolean
}

尝试检查深度嵌套对象或数组中的 property 变化时,仍然需要 deep 选项设置为 true。

const state = reactive({
      
  id: 1, 
  attributes: {
      
    name: "",
  },
});

watch(
  () => state,
  (state, prevState) => {
     
    console.log(
      "not deep ",
      state.attributes.name,
      prevState.attributes.name
    );
  }
);

watch(
  () => state,
  (state, prevState) => {
     
    console.log(
      "deep ",
      state.attributes.name,
      prevState.attributes.name
    );
  },
  {
      deep: true }
);

你可能感兴趣的:(vue3.0,vue3.0,watchEffect,watcheffect,watch)