Форма, которая не забывает черновик: Form Schema + Storage Kit
Длинная форма — многошаговый визард, объёмная анкета — и пользователь случайно закрывает вкладку или обновляет страницу. Без сохранения черновика всё введённое пропадает. Form Schema не хранит состояние формы сама по себе (оно живёт в реактивном values из useForm(), а не где-то ещё), поэтому подключить постоянное хранилище — это просто следить за values и восстанавливать их при инициализации, а не переписывать логику формы.
Код
<script setup lang="ts">
import { watch } from 'vue'
import { useForm } from '@macrulez/vue-form-schema'
import { useStorage } from 'vue-storage-kit'
import type { FieldDefinition } from '@macrulez/vue-form-schema'
const schema: FieldDefinition[] = [
{ type: 'text', name: 'title', label: 'Заголовок', required: true },
{ type: 'textarea', name: 'body', label: 'Текст' },
]
// TTL — черновик старше суток не восстанавливаем: скорее всего, пользователь
// уже не вспомнит, о чём была форма, и лучше начать с чистого листа.
const { value: draft } = useStorage('post-draft', {
defaultValue: {} as Record<string, unknown>,
ttl: 24 * 60 * 60 * 1000,
})
const { values, submit } = useForm({
schema,
initialValues: draft.value,
onSubmit: async (data) => {
const response = await fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) })
if (!response.ok) throw new Error(`Не удалось сохранить: ${response.status}`)
draft.value = {} // черновик отправлен успешно — только теперь очищаем
},
})
// deep: true — values это плоский объект по полям схемы, но поля типа
// 'group'/'array' дают вложенную структуру, точечное отслеживание её не поймает.
watch(values, (v) => (draft.value = { ...v }), { deep: true })
</script>Почему watch с копированием, а не прямое присваивание draft.value = values
values из useForm() — Vue-реактивный объект, живущий внутри composable; draft.value из useStorage() — отдельный Ref, который сериализуется в хранилище при каждой записи. Если присвоить draft.value = values один раз при инициализации, draft будет ссылаться на тот же реактивный объект, но useStorage() не узнает о последующих мутациях его полей — запись в хранилище происходит при присваивании .value, а не при мутации того, на что оно указывает. watch с { ...v } на каждое изменение — простой и явный способ гарантировать, что хранилище всегда видит присваивание, а не мутацию.
Что дальше
- Добавить
debounceв опцииuseStorage()— если черновик пишется на каждое нажатие клавиши, дебаунс избавляет от записи вlocalStorageна каждый символ. См. Storage Kit — обзор. - Для чувствительных данных —
encrypt: { password }уuseStorage(), чтобы черновик не лежал вlocalStorageоткрытым текстом.