VueUse 学习 —— useDebouncedRefHistory

1,376 阅读3分钟

useDebouncedRefHistory 是 Vueuse 库中的一个自定义 Hook,它用于在 ref 对象值的修改后添加 debounced history 记录,并支持撤销和重做操作。这个 Hook 的实现与 useRefHistory 类似,但它提供了一个额外的 debounce 选项来设置记录更改的延迟时间。当值被改变时,debounce 选项会防止频繁记录历史,而是等待指定的时间后才记录历史。这样可以避免在值变化过于频繁的情况下,记录太多的历史。

useDebouncedRefHistory 函数接受两个参数:

  1. source:要记录历史的 Ref 对象。
  2. options:可选参数对象,其中包含了除 eventFilter 以外的 useRefHistoryOptions 配置项,还包括 debounce 配置项,用于设置防抖时间。

函数返回值是一个 UseRefHistoryReturn 对象,其中包含了当前历史记录、撤销和重做方法等等。

用法示例:

import { ref } from 'vue'
import { useDebouncedRefHistory } from '@vueuse/core'

const counter = ref(0)
const { history, undo, redo } = useDebouncedRefHistory(counter, { deep: true, debounce: 1000 })

源码分析

源码地址:

import type { MaybeRefOrGetter } from '@vueuse/shared'
import { debounceFilter } from '@vueuse/shared'
import type { Ref } from 'vue-demi'
import type { UseRefHistoryOptions, UseRefHistoryReturn } from '../useRefHistory'
import { useRefHistory } from '../useRefHistory'

/**
 * Shorthand for [useRefHistory](https://vueuse.org/useRefHistory) with debounce filter.
 *
 * @see https://vueuse.org/useDebouncedRefHistory
 * @param source
 * @param options
 */
export function useDebouncedRefHistory<Raw, Serialized = Raw>(
  source: Ref<Raw>,
  options: Omit<UseRefHistoryOptions<Raw, Serialized>, 'eventFilter'> & { debounce?: MaybeRefOrGetter<number> } = {},
): UseRefHistoryReturn<Raw, Serialized> {
  const filter = options.debounce ? debounceFilter(options.debounce) : undefined
  const history = useRefHistory(source, { ...options, eventFilter: filter })

  return {
    ...history,
  }
}

useDebouncedRefHistory 函数是对 useRefHistory 函数的封装,使用了防抖过滤器来延迟记录历史操作。 在函数内部,根据 options 中的 debounce 配置项是否存在,通过 debounceFilter 函数创建一个防抖过滤器 filter。然后,使用 useRefHistory 函数传入源 source 和配置项(包括防抖过滤器),得到历史记录对象 history

export function debounceFilter(ms: MaybeRefOrGetter<number>, options: DebounceFilterOptions = {}) {
  let timer: ReturnType<typeof setTimeout> | undefined
  let maxTimer: ReturnType<typeof setTimeout> | undefined | null
  let lastRejector: AnyFn = noop

  const _clearTimeout = (timer: ReturnType<typeof setTimeout>) => {
    clearTimeout(timer)
    lastRejector()
    lastRejector = noop
  }

  const filter: EventFilter = (invoke) => {
    const duration = toValue(ms)
    const maxDuration = toValue(options.maxWait)

    if (timer)
      _clearTimeout(timer)

    if (duration <= 0 || (maxDuration !== undefined && maxDuration <= 0)) {
      if (maxTimer) {
        _clearTimeout(maxTimer)
        maxTimer = null
      }
      return Promise.resolve(invoke())
    }

    return new Promise((resolve, reject) => {
      lastRejector = options.rejectOnCancel ? reject : resolve
      // Create the maxTimer. Clears the regular timer on invoke
      if (maxDuration && !maxTimer) {
        maxTimer = setTimeout(() => {
          if (timer)
            _clearTimeout(timer)
          maxTimer = null
          resolve(invoke())
        }, maxDuration)
      }

      // Create the regular timer. Clears the max timer on invoke
      timer = setTimeout(() => {
        if (maxTimer)
          _clearTimeout(maxTimer)
        maxTimer = null
        resolve(invoke())
      }, duration)
    })
  }

  return filter
}

debounceFilter 函数的作用是创建一个防抖过滤器,用于延迟执行回调函数并返回一个 Promise。

具体来说,函数的参数如下:

  • ms:防抖的时间间隔,可以是一个 Ref 对象或一个返回数字的函数。
  • options:可选的配置对象,包括 maxWaitrejectOnCancel

函数内部定义了一些变量,包括 timermaxTimerlastRejector

防抖过滤器的实现逻辑如下:

  1. 首先,将 msoptions.maxWait 转换成实际的数值。
  2. 如果 timer 存在(即已经存在一个定时器),则清除之前的定时器,并调用之前的 lastRejector 函数进行拒绝处理。
  3. 如果 duration(防抖间隔)小于等于 0,或者 maxDuration(最大等待时间)存在且小于等于 0,则立即执行传入的 invoke 回调函数,并返回 Promise 对象解决。
  4. 否则,创建一个 Promise 对象,并在 Promise 构造函数中定义 resolvereject 函数。
  5. lastRejector 设置为 options.rejectOnCancel 为 true 时的 reject 函数,否则为 resolve 函数。
  6. 如果 maxDuration 存在且 maxTimer 为假值(即没有定时器在等待),则创建一个定时器 maxTimer,在达到最大等待时间后执行回调函数,并清除之前的定时器。
  7. 创建一个定时器 timer,在达到防抖间隔后执行回调函数,并在执行时清除 maxTimer
  8. 返回 Promise 对象。

debounceFilter 函数用于创建一个防抖过滤器,该过滤器根据指定的时间间隔和最大等待时间来延迟执行回调函数,并返回一个 Promise 对象。在延迟期间,如果有新的调用发生,会取消之前的延迟操作,并重新计时。