Справочник
Архитектура
vue-storage-kit
│
├── StorageAdapterFactory (синглтон на каждую цель)
│ LocalStorageAdapter → window.localStorage
│ SessionStorageAdapter → window.sessionStorage
│ MemoryStorageAdapter → Map<string, string> (SSR / цель 'memory')
│ IndexedDBStorageAdapter → выделенное object store IndexedDB (цель 'indexeddb')
│
│ StorageAdapter асинхронен (getItem/setItem/removeItem/keys возвращают Promise),
│ так что все четыре бэкенда — включая IndexedDB — используют один конвейер.
│
├── src/engine (фреймворк-агностично — здесь нигде нет импорта vue или react)
│ │
│ ├── StorageEngine
│ │ Владеет полным конвейером чтения/записи: TTL, миграции схемы,
│ │ encrypt/compress/sign, синхронизация вкладок, debounce/throttle,
│ │ история undo/redo, восстановление при превышении квоты (зачистка
│ │ по TTL, затем опциональное LRU-вытеснение). Экспонирует
│ │ getSnapshot()/subscribe() — форму «внешнего хранилища», пригодную
│ │ для любого фреймворка — плюс onEvent() для потребителей в стиле
│ │ таймлайна devtools.
│ │
│ └── engineCache
│ acquireEngine()/releaseEngine() — кэш со счётчиком ссылок, общий
│ для Vue *и* React: два компонента в любом из фреймворков (или
│ обоих), запрашивающих один key+target, получают один StorageEngine.
│
├── composables/useStorage (Vue)
│ Тонкая обёртка: ref/computed, отражающие engine.getSnapshot(),
│ watch(value, flush:'sync'), который вызывает engine.setValue(), и
│ собственный wrapperCache поверх engineCache, чтобы несколько
│ Vue-компонентов делили один и тот же `Ref` (а не только движок).
│
├── react/useStorage (React, точка входа `/react`)
│ Тонкая обёртка: useSyncExternalStore(engine.subscribe, engine.getSnapshot)
│ плюс коллбэк setValue(). Захватывает/освобождает через тот же engineCache.
│
├── SchemaManager
│ Строит и запускает цепочки миграций (up или down)
│
├── TTLManager
│ Проверяет exp при каждом чтении (ленивое истечение)
│ cleanExpired() — массовая зачистка с опциональным префиксом
│
├── createJSONSerializer
│ Обрабатывает Date, Map, Set, undefined через preProcess()
│ (preProcess обходит дерево перед JSON.stringify, чтобы избежать
│ перехвата replacer'а через Date.prototype.toJSON())
│
├── useIndexedDB / useIDBRef (Vue)
│ IndexedDBAdapter — лениво открывает IDB, создаёт store при обновлении
│ useIDBRef следит за ref и вызывает adapter.set() при изменении
│
├── useCookie (Vue)
│ Парсит document.cookie при монтировании
│ watch → строит строку Set-Cookie и присваивает её document.cookie
│
├── /crypto (отдельная точка входа)
│ StorageEncryption — encrypt() / decrypt() / reencrypt() / rotateEncryptedKey()
│ Вывод ключа через PBKDF2; выведенные ключи кэшируются по (password, iterations, salt)
│ StorageSigning — sign() / verify(), HMAC-SHA256
│
├── /sync (отдельная точка входа)
│ LeaderElection — navigator.locks; держит блокировку на время жизни вкладки
│ TabSync — BroadcastChannel + запасной вариант через событие storage
│ последняя запись побеждает по временной метке; при ничьей побеждает лидер
│
├── VueStoragePlugin (Vue)
│ prefix/defaultTarget/defaultSerializer/defaultEncrypt/onError, читается
│ каждым вызовом Vue useStorage() после установки через getGlobalOptions()
│
├── /devtools (отдельная точка входа, опционально через setupDevtools(app))
│ Инспектор + таймлайн поверх engineCache — видит экземпляры и Vue, и React
│
├── /testing (отдельная точка входа)
│ mockStorage()/resetStorageState()/seedEnvelope()/flushAsync()
│
└── Модуль Nuxt (vue-storage-kit/nuxt)
addImports — автоимпорт всех composables (useCookie → SSR-совместимая runtime-версия)
addPlugin — устанавливает VueStoragePlugin с runtimeConfig.storageKit.prefixРазмер бандла и peer-зависимости
| Точка входа | Нужен ли vue? | Peer/runtime-зависимости | Заметки |
|---|---|---|---|
vue-storage-kit | Да | vue ^3.3 | Vue composables, плагин, адаптеры, сериализатор |
vue-storage-kit/react | Нет | react ^18 | React-хук useStorage() |
vue-storage-kit/crypto | Нет | — | AES-GCM шифрование, HMAC-подписи, смена ключа |
vue-storage-kit/sync | Нет | — | Только TabSync и LeaderElection |
vue-storage-kit/compress | Нет | — | Только хелперы Compression Streams + CompressAdapter |
vue-storage-kit/pinia | Нет | pinia ^2 | ^3 (опциональная peer) | Только createPiniaPersist |
vue-storage-kit/devtools | Нет | @vue/devtools-api (входит как runtime-зависимость) | Инспектор + таймлайн, опционально |
vue-storage-kit/testing | Да¹ | — | Хелперы для тестов |
vue-storage-kit/nuxt | — | @nuxt/kit (опциональная peer), h3 (опциональная peer) | Модуль Nuxt |
¹ /testing подключает Vue composable-модуль ради хелпера сброса кэша, даже если вы тестируете только React-код — это стоит только на dev/test, никогда не попадает в продакшен, поэтому не оптимизируется.
Пакет поставляется как tree-shakeable ESM (dist/index.js) и CommonJS (dist/index.cjs). Точки входа /crypto, /sync и /compress также разбиты на отдельные чанки внутри useStorage — загружаются динамически только когда опции encrypt, sync или compress реально заданы, сохраняя базовый вес маленьким независимо от того, какая точка входа их подключила. @vue/devtools-api — единственная обязательная runtime-зависимость пакета, и она никогда не включается в ., /react или /nuxt — загружается только если вы явно импортируете vue-storage-kit/devtools и сами вызываете setupDevtools(app). Ни vue, ни react не являются жёсткой зависимостью пакета в целом — только той конкретной точки входа, которую вы импортируете.
Сравнение с @vueuse/core
vue-storage-kit расширяет и расходится с @vueuse/core в конкретных областях.
Прямые замены
| @vueuse/core | vue-storage-kit | Заметки |
|---|---|---|
useLocalStorage(key, default) | useLocalStorage(key, default) | Та же сигнатура; flush: 'sync' по умолчанию |
useSessionStorage(key, default) | useSessionStorage(key, default) | Та же сигнатура |
useCookies() | useCookie(name, options) | Реактивный Ref для каждой cookie вместо одного объекта |
useStorageAsync() | useIDBRef() | Реактивный Ref на базе асинхронного хранилища (IndexedDB) |
useBroadcastChannel() | useBroadcastChannel() | Идентичный API |
Расширенная функциональность (нет аналога в vueuse)
| Возможность | vue-storage-kit |
|---|---|
| Миграции схемы | Опция migrations: [{ version, up, down? }] в StorageOptions |
| TTL / истечение | Опция ttl (секунды); ленивая проверка при чтении; без фоновых таймеров |
| AES-GCM шифрование | Опция encrypt: { password }; только Web Crypto API, без дополнительных зависимостей |
| Синхронизация вкладок | Опция sync: true; BroadcastChannel с запасным вариантом через событие storage |
| Выборы лидера | Лидер на базе navigator.locks в LeaderElection |
| Полный API IndexedDB | useIndexedDB() — get / set / delete / keys / getAll / transaction / индексы |
| Вторичные индексы IDB | useIndexedDB('db', 'store', onError, { indexes: [...] }) |
| CRUD-коллекция | useStorageList<T>() — add / update / remove / find / findAll |
| Персистентность Pinia | Точка входа /pinia — createPiniaPersist({ pick?, omit? }) |
| Сжатие | Опция compress: true на useStorage, либо самостоятельная точка входа /compress — compress() / decompress() через Compression Streams API |
| Экспорт / импорт | exportStorage() / importStorage() — снапшот и восстановление всех ключей |
| Общий кэш экземпляров | Два компонента (Vue или React), вызывающие useStorage('key'), делят один движок — ноль дублирующихся наблюдателей/таймеров |
| Инспектор + таймлайн Devtools | Точка входа /devtools — setupDevtools(app), видит экземпляры и Vue, и React |
| HMAC-подписи | Опция sign: { password } — обнаружение случайного повреждения без требования секретности |
| Undo / redo | Опция history: n — undo()/redo() в памяти, без дополнительного управления состоянием |
| Throttle | Опция throttle, наряду с debounce |
| Восстановление при превышении квоты | Автоматическая зачистка по TTL + повтор; опциональный evictOnQuota для LRU-вытеснения других ключей |
| Поддержка React | Точка входа /react — те же опции, тот же движок, на базе useSyncExternalStore |
| Утилиты для тестирования | Точка входа /testing — mockStorage(), resetStorageState(), seedEnvelope() |
Отличия в поведении
| Поведение | @vueuse/core | vue-storage-kit |
|---|---|---|
| Flush наблюдателя | 'pre' (по умолчанию у Vue) | 'sync' — запись происходит в той же микрозадаче, что и присваивание |
| Обновление между вкладками | Только событие storage | BroadcastChannel с запасным вариантом через событие storage |
| Сериализация | Только JSON | JSON + round-trip для Date, Map, Set, BigInt; кастомный Serializer<T> |
| Несколько экземпляров | Независимые наблюдатели на каждый вызов | Общий StorageEngine через кэш со счётчиком ссылок, между Vue и React |
| SSR | Глобальные заглушки | Те же заглушки; useCookie принимает событие H3 для серверных маршрутов Nuxt |
Лицензия
MIT