Skip to content

Надёжность и синхронизация ​

Устойчивость и производительность ​

Debounce и throttle ​

ts
// Значение записывается только через 500мс после остановки ввода.
const { value: draft } = useStorage('draft', { defaultValue: '', debounce: 500 })

// Пишется не чаще раза в 200мс, пока перетаскивается слайдер, вместо
// ожидания остановки.
const { value: volume } = useStorage('volume', { defaultValue: 50, throttle: 200 })

Они взаимоисключающие — если заданы оба, побеждает throttle, и при создании один раз выводится предупреждающий console.warn в dev-режиме. В любом случае запись, всё ещё ожидающая при размонтировании компонента, сбрасывается немедленно, а не отбрасывается.

Восстановление при превышении квоты ​

При QuotaExceededError useStorage сначала зачищает истёкшие по TTL записи этого адаптера (по той же логике, что и TTLManager.cleanExpired()) и повторяет попытку один раз. Если этого недостаточно, включите evictOnQuota, чтобы дополнительно вытеснять наименее недавно записанные другие ключи того же адаптера:

ts
const { value } = useStorage('cache-entry', {
  defaultValue: null,
  evictOnQuota: { max: 3 }, // вытеснить до 3 других ключей перед тем, как сдаться
})

Вытеснение может оценивать возраст только обычных (незашифрованных, несжатых) конвертов — оно оставляет в покое зашифрованные/сжатые/подписанные ключи, принадлежащие другим вызовам useStorage(), поскольку их возраст нельзя безопасно проверить без их собственных ключей.

Сбои записи, не связанные с квотой (полный диск, сломанный адаптер), сообщаются как { type: 'write-failed', key, error } через onError — они никогда не выбрасываются изнутри внутренней реактивной записи, что иначе было бы необработанным отклонением промиса, которое ваше приложение никогда не увидит.

Undo / redo ​

ts
const {
  value: text,
  undo,
  redo,
  canUndo,
  canRedo,
} = useStorage('editor-content', {
  defaultValue: '',
  history: 20, // хранить последние 20 значений
})

text.value = 'draft one'
text.value = 'draft two'
undo() // text.value === 'draft one'
redo() // text.value === 'draft two'

История живёт только в памяти — она не сохраняется между перезагрузками и сама по себе не пишется в хранилище. canUndo/canRedo реактивны, так что вы можете отключать соответствующие кнопки в UI.

Синхронизация вкладок ​

Когда sync: true, записи в value рассылаются всем остальным открытым вкладкам через BroadcastChannel. Удалённые обновления применяются молча (без обратной записи в хранилище). Откатывается на window.addEventListener('storage', ...), если BroadcastChannel недоступен.

ts
const { value: cart } = useStorage('cart', {
  defaultValue: [] as CartItem[],
  sync: true,
})
// cart.value автоматически остаётся синхронизированным во всех вкладках

SyncOptions ​

channel ​

string · по умолчанию: 'vue-storage-kit'

Имя BroadcastChannel.

leader ​

boolean · по умолчанию: false

Включить выборы лидера — только одна вкладка пишет в хранилище при конфликте.

debounce ​

number · по умолчанию: 50

Миллисекунды для дебаунса исходящих рассылок.

Выборы лидера используют navigator.locks. Вкладка-лидер удерживает именованную блокировку на всё время своей жизни. Когда лидер закрывается, другая вкладка автоматически захватывает блокировку и становится новым лидером. При leader: true конфликты разрешаются по принципу «последняя запись побеждает» по временной метке — при ничьей сохраняется версия лидера.

ts
const { value: sharedState } = useStorage('shared', {
  defaultValue: { count: 0 },
  sync: { channel: 'app-sync', leader: true, debounce: 100 },
})

Использование TabSync напрямую ​

ts
import { TabSync } from 'vue-storage-kit/sync'

const sync = new TabSync({ channel: 'custom-channel', leader: true })
await sync.start()

sync.subscribe('my-key', (rawValue) => {
  console.log('Received from another tab:', rawValue)
})

sync.broadcast('my-key', JSON.stringify({ count: 1 }), Date.now())
sync.stop()

useBroadcastChannel ​

Низкоуровневая, обобщённая примитива pub/sub поверх BroadcastChannel — вообще не привязана к useStorage(), в отличие от опции sync выше. Используйте её для обмена сообщениями между вкладками, не связанного с сохранением ключа хранилища (живой чат между вкладками, тост «кто-то другой внёс изменения», координация общего таймера).

ts
function useBroadcastChannel<T>(channelName: string): UseBroadcastChannelReturn<T>

isSupported ​

boolean

Существует ли BroadcastChannel в этом окружении. При false post() становится тихим no-op, и сообщения никогда не доходят.

lastMessage ​

ShallowRef<T | null>

Последнее полученное сообщение.

messages ​

Ref<T[]>

Все полученные до сих пор сообщения, от старых к новым.

post ​

(message: T) => void

Отправляет сообщение всем остальным вкладкам/окнам, подписанным на тот же channelName.

close ​

() => void

Закрывает базовый канал. Запускается автоматически при уничтожении scope, если вызван внутри активного effect scope.

ts
import { useBroadcastChannel } from 'vue-storage-kit'

interface ChatMessage {
  user: string
  text: string
}

const { lastMessage, post } = useBroadcastChannel<ChatMessage>('team-chat')

post({ user: 'ada', text: 'Hello from this tab' })
// lastMessage.value обновляется на каждой другой вкладке, подписанной на 'team-chat'