useRefHistory
ref の変更履歴を追跡し、元に戻す/やり直し機能も提供します。
Vue Schoolの無料ビデオレッスンでuseRefHistoryを学びましょう!デモ
使用方法
import { useRefHistory } from '@vueuse/core'
import { ref } from 'vue'
const counter = ref(0)
const { history, undo, redo } = useRefHistory(counter)
内部的には、`watch` を使用して、ref 値が変更されたときに履歴ポイントをトリガーします。つまり、履歴ポイントは同じ「ティック」での変更を非同期にバッチ処理してトリガーされます。
counter.value += 1
await nextTick()
console.log(history.value)
/* [
{ snapshot: 1, timestamp: 1601912898062 },
{ snapshot: 0, timestamp: 1601912898061 }
] */
`undo` を使用して、ref 値を最後の履歴ポイントにリセットできます。
console.log(counter.value) // 1
undo()
console.log(counter.value) // 0
オブジェクト/配列
オブジェクトまたは配列を扱う場合、属性を変更しても参照は変更されないため、コミットはトリガーされません。属性の変更を追跡するには、 `deep: true` を渡す必要があります。これにより、履歴レコードごとにクローンが作成されます。
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を使用します
import { useRefHistory } from '@vueuse/core'
const refHistory = useRefHistory(target, { clone: structuredClone })
または、lodashの `cloneDeep`を使用します
import { useRefHistory } from '@vueuse/core'
import { cloneDeep } from 'lodash-es'
const refHistory = useRefHistory(target, { clone: cloneDeep })
または、より軽量な `klona`を使用します
import { useRefHistory } from '@vueuse/core'
import { klona } from 'klona'
const refHistory = useRefHistory(target, { clone: klona })
カスタムダンプおよび解析関数
`clone` オプションを使用する代わりに、カスタム関数を渡してシリアル化と解析を制御できます。履歴値がオブジェクトである必要がない場合は、元に戻すときに余分なクローンを節約できます。また、スナップショットをすでに文字列化してローカルストレージに保存する場合などに役立ちます。
import { useRefHistory } from '@vueuse/core'
const refHistory = useRefHistory(target, {
dump: JSON.stringify,
parse: JSON.parse,
})
履歴容量
明示的にクリアしない限り、すべての履歴をデフォルトで(無制限に)保持します。`capacity` オプションで保持する履歴の最大量を設定できます。
const refHistory = useRefHistory(target, {
capacity: 15, // limit to 15 history records
})
refHistory.clear() // explicitly clear all the history
履歴フラッシュタイミング
Vueのドキュメントより:Vueのリアクティビティシステムは、無効化されたエフェクトをバッファリングし、同じ「ティック」で多くの状態変化が発生した場合に不要な重複呼び出しを回避するために、非同期にフラッシュします。
`watch` と同じ方法で、 `flush` オプションを使用してフラッシュタイミングを変更できます。
const refHistory = useRefHistory(target, {
flush: 'sync', // options 'pre' (default), 'post' and 'sync'
})
デフォルトは `'pre'` で、このコンポーザブルをVueのウォッチャーのデフォルトと一致させます。これは、アプリの状態の不変性を壊す可能性のあるref値への複数ステップ更新の一部として生成される複数の履歴ポイントなど、一般的な問題を回避するのにも役立ちます。同じ「ティック」で複数の履歴ポイントを作成する必要がある場合は、 `commit()` を使用できます
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 },
] */
一方、フラッシュ `'sync'` を使用する場合、 `batch(fn)` を使用して、複数の同期操作に対して単一の履歴ポイントを生成できます
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 }` が使用されている場合、 `batch` は配列で可変の `splice` を実行する場合にも役立ちます。 `splice` は、ref 履歴にプッシュされる最大3つのアトミック操作を生成できます。
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値を変更しないようにすることです。
推奨読書
型宣言
型宣言を表示
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.dokyumento.jp/useRefHistory
* @param source
* @param options
*/
export declare function useRefHistory<Raw, Serialized = Raw>(
source: Ref<Raw>,
options?: UseRefHistoryOptions<Raw, Serialized>,
): UseRefHistoryReturn<Raw, Serialized>