跳至内容

useRefHistory

类别
导出大小
1.45 kB
上次更改
上周
相关

跟踪 ref 的更改历史记录,还提供撤消和重做功能

通过 Vue School 的此免费视频课程学习 useRefHistory!

演示

计数:0
/

历史记录(演示版限制为 10 条记录)
2024-11-28 03:59:08{ value: 0 }

用法

ts
import { useRefHistory } from '@vueuse/core'
import { ref } from 'vue'

const counter = ref(0)
const { history, undo, redo } = useRefHistory(counter)

在内部,watch 用于在 ref 值修改时触发历史记录点。这意味着历史记录点是异步触发的,将同一“tick”中的修改批处理。

ts
counter.value += 1

await nextTick()
console.log(history.value)
/* [
  { snapshot: 1, timestamp: 1601912898062 },
  { snapshot: 0, timestamp: 1601912898061 }
] */

您可以使用 undo 将 ref 值重置为上一个历史记录点。

ts
console.log(counter.value) // 1
undo()
console.log(counter.value) // 0

对象/数组

在处理对象或数组时,由于更改其属性不会更改引用,因此不会触发提交。要跟踪属性更改,您需要传递 deep: true。它将为每个历史记录创建克隆。

ts
const state = ref({
  foo: 1,
  bar: 'bar',
})

const { history, undo, redo } = useRefHistory(state, {
  deep: true,
})

state.value.foo = 2

await nextTick()
console.log(history.value)
/* [
  { snapshot: { foo: 2, bar: 'bar' } },
  { snapshot: { foo: 1, bar: 'bar' } }
] */

自定义克隆函数

useRefHistory 仅嵌入最小的克隆函数 x => JSON.parse(JSON.stringify(x))。要使用功能齐全或自定义的克隆函数,您可以通过 clone 选项进行设置。

例如,使用 structuredClone

ts
import { useRefHistory } from '@vueuse/core'

const refHistory = useRefHistory(target, { clone: structuredClone })

或者使用 lodash 的 cloneDeep

ts
import { useRefHistory } from '@vueuse/core'
import { cloneDeep } from 'lodash-es'

const refHistory = useRefHistory(target, { clone: cloneDeep })

或者更轻量级的 klona

ts
import { useRefHistory } from '@vueuse/core'
import { klona } from 'klona'

const refHistory = useRefHistory(target, { clone: klona })

自定义转储和解析函数

您可以传递自定义函数来控制序列化和解析,而不是使用 clone 选项。如果您不需要历史记录值为对象,这可以节省撤消时的额外克隆。如果您希望快照已字符串化以保存到本地存储,它也很有用。

ts
import { useRefHistory } from '@vueuse/core'

const refHistory = useRefHistory(target, {
  dump: JSON.stringify,
  parse: JSON.parse,
})

历史记录容量

默认情况下,我们将保留所有历史记录(无限),直到您明确清除它们为止,您可以通过 capacity 选项设置要保留的历史记录的最大数量。

ts
const refHistory = useRefHistory(target, {
  capacity: 15, // limit to 15 history records
})

refHistory.clear() // explicitly clear all the history

历史记录刷新时间

来自 Vue 的文档:Vue 的响应式系统缓冲无效的 effect 并异步刷新它们,以避免在同一“tick”中发生许多状态突变时不必要的重复调用。

watch 一样,您可以使用 flush 选项修改刷新时间。

ts
const refHistory = useRefHistory(target, {
  flush: 'sync', // options 'pre' (default), 'post' and 'sync'
})

默认值为 'pre',以使此组合与 Vue 的观察者的默认值保持一致。这也有助于避免常见问题,例如作为对 ref 值的多步更新的一部分生成的多个历史记录点,这可能会破坏应用程序状态的不变量。如果需要在同一“tick”中创建多个历史记录点,可以使用 commit()

ts
const r = ref(0)
const { history, commit } = useRefHistory(r)

r.value = 1
commit()

r.value = 2
commit()

console.log(history.value)
/* [
  { snapshot: 2 },
  { snapshot: 1 },
  { snapshot: 0 },
] */

另一方面,当使用 `flush: 'sync'` 时,可以使用 `batch(fn)` 为多个同步操作生成一个历史记录点。

ts
const r = ref({ names: [], version: 1 })
const { history, batch } = useRefHistory(r, { flush: 'sync' })

batch(() => {
  r.value.names.push('Lena')
  r.value.version++
})

console.log(history.value)
/* [
  { snapshot: { names: [ 'Lena' ], version: 2 },
  { snapshot: { names: [], version: 1 },
] */

如果使用了 `{ flush: 'sync', deep: true }`,在数组中进行可变 `splice` 操作时,`batch` 也很有用。`splice` 可以生成最多三个原子操作,这些操作将被推送到 ref 历史记录中。

ts
const arr = ref([1, 2, 3])
const { history, batch } = useRefHistory(arr, { deep: true, flush: 'sync' })

batch(() => {
  arr.value.splice(1, 1) // batch ensures only one history point is generated
})

另一种选择是避免修改原始 ref 值,可以使用 `arr.value = [...arr.value].splice(1,1)`。

类型声明

显示类型声明
typescript
export interface UseRefHistoryOptions<Raw, Serialized = Raw>
  extends ConfigurableEventFilter {
  /**
   * Watch for deep changes, default to false
   *
   * When set to true, it will also create clones for values store in the history
   *
   * @default false
   */
  deep?: boolean
  /**
   * The flush option allows for greater control over the timing of a history point, default to 'pre'
   *
   * Possible values: 'pre', 'post', 'sync'
   * It works in the same way as the flush option in watch and watch effect in vue reactivity
   *
   * @default 'pre'
   */
  flush?: "pre" | "post" | "sync"
  /**
   * Maximum number of history to be kept. Default to unlimited.
   */
  capacity?: number
  /**
   * Clone when taking a snapshot, shortcut for dump: JSON.parse(JSON.stringify(value)).
   * Default to false
   *
   * @default false
   */
  clone?: boolean | CloneFn<Raw>
  /**
   * Serialize data into the history
   */
  dump?: (v: Raw) => Serialized
  /**
   * Deserialize data from the history
   */
  parse?: (v: Serialized) => Raw
}
export interface UseRefHistoryReturn<Raw, Serialized>
  extends UseManualRefHistoryReturn<Raw, Serialized> {
  /**
   * A ref representing if the tracking is enabled
   */
  isTracking: Ref<boolean>
  /**
   * Pause change tracking
   */
  pause: () => void
  /**
   * Resume change tracking
   *
   * @param [commit] if true, a history record will be create after resuming
   */
  resume: (commit?: boolean) => void
  /**
   * A sugar for auto pause and auto resuming within a function scope
   *
   * @param fn
   */
  batch: (fn: (cancel: Fn) => void) => void
  /**
   * Clear the data and stop the watch
   */
  dispose: () => void
}
/**
 * Track the change history of a ref, also provides undo and redo functionality.
 *
 * @see https://vueuse.org.cn/useRefHistory
 * @param source
 * @param options
 */
export declare function useRefHistory<Raw, Serialized = Raw>(
  source: Ref<Raw>,
  options?: UseRefHistoryOptions<Raw, Serialized>,
): UseRefHistoryReturn<Raw, Serialized>

源代码

源代码演示文档

贡献者

Anthony Fu
Matias Capeletto
Anthony Fu
Lov`u`e
Kyrie890514
sun0day
Eduardo Wesley
vaakian X
Hollis Wu
Bruno Perel
wheat
webfansplz
Roman Harmyder
Alex Kozack
Antério Vieira

更新日志

v12.0.0-beta.1 于 2024年11月21日
0a9ed - feat!: 移除 Vue 2 支持,优化包体积并清理代码 (#4349)
v10.8.0 于 2024年2月20日
a086e - fix: 更严格的类型

在 MIT 许可证下发布。