Лучший способ работы с хранилищем веб-клиента
Браузеры оснащены опциями хранения, которые помогают вам управлять данными на клиенте, чтобы свести к минимуму вызовы на сервер и даже обеспечить автономную работу в вашем приложении. Некоторые приложения даже изначально загружали данные с сервера и работали без необходимости после этого вызывать сервер.
Дело не в том, нужно ли вам иметь дело с вариантами клиентского хранилища, а в том, когда. Однако, когда вы прыгаете в этот мир, возникают некоторые проблемы:
- Каждый из них поставляется со своим собственным API, а некоторые из них являются подробными;
- Каждый из них имеет свои ограничения по типам данных, что означает, что не все типы данных могут быть сохранены;
- Скорее всего, вам придется вернуться к другим вариантам, потому что не все типы и версии браузеров поддерживают все варианты хранения;
- Скорее всего, вам потребуется управлять форматом, проверкой и типом данных, чтобы обеспечить согласованное хранение данных и предотвратить проверки данных на уровне приложения.
- Скорее всего, вам понадобится библиотека, чтобы помочь, но найти такую, которая решит все или большинство ваших проблем, скорее всего, будет непростой задачей.
Входит в пакет «клиент-веб-хранилище»
клиент-веб-хранилище — многообещающее решение, потому что:
- Он предоставляет единый и простой API для работы со всеми вариантами клиентского хранилища: IndexedDb, WebSQL, LocalStorage и в памяти;
- Он автоматически переключается на различные параметры, гарантируя, что ваши данные будут правильно храниться на клиенте;
- Поддерживает длинный список типов данных:
Date, Number, String, Boolean, Array, ArrayBuffer, Blob, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array
; - Он выполняет базовую проверку данных для вас;
- Поставляется с поддержкой схемы, которая гарантирует формат данных. Он автоматически создает для вас поля со значениями по умолчанию;
- Предоставляет перехватчики для связи API вашего сервера, поэтому вы взаимодействуете только с хранилищем данных, в то время как все связи API происходят сзади;
- Он предлагает невероятно простой API для всего;
- Позволяет вкладывать схемы для создания сложных хранилищ;
Как это работает?
Он позволяет вам определять небольшие документы с определенным форматом данных через определение схемы. Это ваш способ сообщить ему, как выглядят данные и как они должны выполнять проверку. Позволь мне показать тебе:
Допустим, у меня есть проект TypeScript, поэтому мне нужно определить тип для моих данных:
interface User extends Schema.DefaultValue { name: string; } interface ToDo extends Schema.DefaultValue { name: string; description: string; complete: boolean; user: User; }
Выше я определяю User
и ToDo
, которые расширяют Schema.DefaultValue
, которые определяют поля id
, createdDate
и lastUpdatedDate
, которые являются важными полями данных, которые обрабатываются автоматически для вас. Он сгенерирует для вас uuid
и обновит поля lastUpdatedDate
при внесении изменений.
Определить схему
Из этого я могу определить свою схему, которую я также могу добавить к ней:
const userSchema = new Schema<User>("user", { name: new SchemaValue(String, true), }); const todoShema = new Schema<ToDo>("todo", { name: new SchemaValue(String, true), description: new SchemaValue(String, false, "No Description"), complete: new SchemaValue(Boolean), })
Выше я определяю схему пользователя с обязательным полем имени:
new SchemaValue(type, isRequired, defaultValue)
Затем я определяю схему задач с обязательным полем имени и необязательным полем описания, которое, если значение не указано, будет иметь значение по умолчанию No Description
, и, наконец, логическое поле complete
, чтобы указать, был ли элемент задачи завершен.
Одна классная вещь, которую я могу сделать со схемами, — это автоматически сгенерировать базовый объект данных со значениями по умолчанию:
const todo1 = todoShema.toValue(); /* todo1 { id: "123e4567-e89b-12d3-a456-426614174000", name: "", description: "No Description", complete: false, createdDate: "January, 4th 2022", lastUpdatedDate: "January, 4th 2022", } */
Он создаст uuid
, добавит даты и использует значения по умолчанию для полей, которые я определил в схеме.
Создать хранилище данных
Волшебство происходит, когда вы создаете свой магазин.
const todoStore = new ClientStore<ToDo>("todos", todoSchema)
Это самая простая форма создания хранилища, которое принимает все значения по умолчанию. Это означает, что он обрабатывает версии, тип хранилища, резервные копии, создание, оптимизацию и т. д.
Я также могу предоставить свою конфигурацию, которая состоит из 4 вариантов:
const todoStore = new ClientStore<ToDo>("todos", todoSchema, { appName: "My Todo App", version: 1, // changes when configuration or schema changes type: ClientStore.Type.LOCALSTORAGE, description: "manages user todos" })
Выше я создаю первую версию моего магазина задач, который я определяю как тип LOCALSTORAGE
.
Я также могу предоставить список типов, к которым он может вернуться, например:
type: [ ClientStore.Type.INDEXEDDB, ClientStore.Type.LOCALSTORAGE, ClientStore.Type.MEMORY_STORAGE ],
Вышеупомянутое означает, попробуйте эти варианты хранения в соответствии с поддержкой браузера, начиная с INDEXEDDB
до MEMORY_STORAGE
.
Магазин многое делает для вас. Он будет читать хранилище клиентов при загрузке, если они существуют, заполнять все и быть готовым к вашему взаимодействию с ним. Магазин на 100% асинхронный, что очень помогает.
CRUD магазин
Создать предмет очень просто. Мне не нужно указывать все значения. Я могу просто указать значения по умолчанию, а остальные заполняются автоматически:
const todo1 = await todoStore.createItem({ name: "Go Shopping", // name is required }); /* Will create item { id: "123e4567-e89b-12d3-a456-426614174000", name: "Go shopping", description: "No Description", complete: false, createdDate: "January, 4th 2022", lastUpdatedDate: "January, 4th 2022", } */
Он также проверит данные на соответствие типу и типу данных схемы, который я определил. Например, выполнение следующего не удастся:
todoStore.createItem({}); todoStore.createItem({name: 12}); todoStore.createItem({complete: "yes"});
Для меня он проверяет тип и требования к полям. Он также максимально фильтрует и отображает данные для меня.
const todo = fetchSingleTodo("984728374834"); todoStore.createItem(todo);
Если данные, возвращаемые из API, содержат больше или меньше полей, которые ему нужны, он всегда будет добавлять только те поля, которые необходимы, возвращаясь к значениям по умолчанию, когда имя поля не существует в предоставленных вами данных.
Обновление магазина — аналогичный процесс:
const todo1 = await todoStore.createItem({ name: "Go Shopping", }); await todoStore.updateItem(todo1.id, { complete: true })
В общем, он поддерживает только следующие CRUD и ищет асинхронные действия: createItem, updateItem, getItem, getItems, loadItems, removeItem, clear, findItem, findItems
.
Магазинные крючки
Можно узнать и перехватить все, что делается в магазине.
Вы можете подписаться на обновления магазина:
const unsub = todoStore.subscribe((eventType, details) => { switch (eventType) { case ClientStore.EventType.READY: // handle event type here break; case ClientStore.EventType.CREATED: // handle event type here break; case ClientStore.EventType.UPDATED: // handle event type here break; case ClientStore.EventType.ABORTED: // handle event type here break; case ClientStore.EventType.CLEARED: // handle event type here break; case ClientStore.EventType.DELETED: // handle event type here break; case ClientStore.EventType.ERROR: // handle event type here break; default: } }) unsub() // call to unsubscribe from the store
Предоставленный вами обработчик подписки вызывается с типом события и данными, полученными в результате этого типа действия.
Вы также можете перехватывать действия до того, как они произойдут в магазине, и даже прерывать их, чтобы они не происходили. Это идеальный хук для обработки API вашего сервера.
const unsub = todoStore.beforeChange(async (eventType, data) => { switch (eventType) { case ClientStore.EventType.CREATED: await todoService.createTodo(data); break; case ClientStore.EventType.UPDATED: await todoService.updateTodo(data.id, data); break; case ClientStore.EventType.DELETED: await todoService.updateTodo(data); break; case ClientStore.EventType.CLEARED: await todoService.deleteAllByIds(data); break; default: }; return true; }); unsub();
Вы можете вернуть true или false независимо от того, следует ли обновлять хранилище, которое отправит событие ABORT
, которое вы можете перехватить в обработчике подписки.
Вы также можете выдавать ошибки или просто позволить вашему API дать сбой, и магазин будет перехватывать и транслировать событие ERROR
, которое вы также можете перехватить в обработчике подписки.
Более сложные данные
Отличительной особенностью схемы является то, что вы можете вкладывать их друг в друга для формирования сложных хранилищ.
Например, мы можем вложить схему пользователя в схему задач.
// Schema TS type interface User extends Schema.DefaultValue { name: string; } interface ToDo extends Schema.DefaultValue { name: string; description: string; complete: boolean; user: User; } // denife schemas const userSchema = new Schema<User>("user"); const todoSchema = new Schema<ToDo>("todo"); userSchema.defineField("name", String, {required: true}); todoShema.defineField("name", String, {required: true}); todoShema.defineField("description", String); todoShema.defineField("complete", Boolean); todoShema.defineField("user", userSchema, {required: true}); // create stores const userStore = new ClientStore<User>("users", userSchema); const todoStore = new ClientStore<ToDo>("todos", todoSchema);
Тогда создание элемента будет выглядеть так:
// you can create a new on or get it from the store const AdminUser = await userStore.createItem({ name: "John Doe" }); const todo1 = await todoStore.createItem({ name: "Go Shopping", user: AdminUser }); /* Will create item { id: "123e4567-e89b-12d3-a456-426614174000", name: "Go shopping", description: "No Description", complete: false, user: { id: 3483748929e82382, name: "John Doe", createdDate: "January, 4th 2022", lastUpdatedDate: "January, 4th 2022", } createdDate: "January, 4th 2022", lastUpdatedDate: "January, 4th 2022", } */
Хранилище будет глубоко оценивать данные в соответствии с определением схемы, поэтому следующее не удастся:
// missing required user await todoStore.createItem({name: "Buy shoes"}); // missing required user name await todoStore.createItem({name: "Buy shoes", user: {}});
Еда на вынос
Это решение избавляет вас от суеты при работе с данными на клиенте. Простой факт, что он обрабатывает множество вариантов хранения с одним и тем же интерфейсом API, является огромным плюсом. Помимо данных, он поддерживает как простые, так и сложные типы данных, формат данных и проверки с удивительными хуками, чтобы действительно извлечь из этого максимальную пользу.
Проверьте это, и дайте мне знать, что вы думаете в комментариях:
Канал YouTube: Before Semicolon
Веб-сайт: beforesemicolon.com