跳到主要内容

useRefHistory

分类
导出大小
1.2 kB
上次更改
上个月
相关

跟踪 ref 的更改历史,并提供撤消和重做功能。

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

演示

计数:0
/

历史记录 (演示限制为 10 条)
2025-10-23 06:49:07{ value: 0 }

用法

ts
import { 
useRefHistory
} from '@vueuse/core'
import {
shallowRef
} from 'vue'
const
counter
=
shallowRef
(0)
const {
history
,
undo
,
redo
} =
useRefHistory
(
counter
)

在内部,当 ref 值被修改时,使用 watch 来触发历史记录点。这意味着历史记录点是异步触发的,并在同一“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

历史 WatchOptionFlush 时机

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

watch 相同,您可以使用 flush 选项修改刷新时机。

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

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

ts
const 
r
=
shallowRef
(0)
const {
history
,
commit
} =
useRefHistory
(
r
)
r
.
value
= 1
commit
()
r
.
value
= 2
commit
()
console
.
log
(
history
.
value
)
/* [ { snapshot: 2 }, { snapshot: 1 }, { snapshot: 0 }, ] */

另一方面,当使用 '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
})

另一个选项是避免使用 arr.value = [...arr.value].splice(1,1) 来改变原始 ref 值。

类型声明

显示类型声明
ts
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
?:
WatchOptionFlush
/** * 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
/** * Function to determine if the commit should proceed * @param oldValue Previous value * @param newValue New value * @returns boolean indicating if commit should proceed */
shouldCommit
?: (
oldValue
:
Raw
| undefined,
newValue
:
Raw
) => boolean
} 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
Vida Xie
Arthur Darkstone
SerKo
IlyaL
Jonathan Schneider
Robin
IlyaL
Lov`u`e
Kyrie890514
sun0day
Eduardo Wesley
vaakian X
Hollis Wu
Bruno Perel
wheat
webfansplz
Roman Harmyder
Alex Kozack
Antério Vieira

更新日志

v13.4.0
18acf - feat: 添加 shouldCommit (#4471)
0a9ed - feat!: 放弃对 Vue 2 的支持,优化打包并清理 (#4349)
v10.8.0
a086e - fix: 更严格的类型

根据 MIT 许可证发布。