Idb-keyval — это минималистичная обёртка над IndexedDB,
которая упрощает операции хранения данных в браузере. Основные методы
включают get, set, del,
clear и keys. Для организации пагинации без
встроенных индексов важно понимать, что IndexedDB не гарантирует порядок
ключей по умолчанию. Idb-keyval сохраняет данные в
объектном хранилище с уникальными ключами, а перечисление всех ключей
через keys() возвращает их в порядке добавления, что можно
использовать для реализации собственного механизма постраничного
отображения.
Для реализации пагинации без индексов рекомендуется использовать следующие подходы:
Последовательные ключи Присваивать ключи в виде
числовых последовательностей (1, 2,
3 …) или строковых с префиксом (item_1,
item_2). Это позволит извлекать диапазоны значений по
ключам, моделируя страницы.
Массивы ключей для каждой страницы Хранить отдельный объект, содержащий массив ключей для каждой страницы. Например:
import { set, get } from 'idb-keyval';
async function savePage(pageNumber, items) {
const keys = [];
for (let i = 0; i < items.length; i++) {
const key = `item_${pageNumber}_${i}`;
await set(key, items[i]);
keys.push(key);
}
await set(`page_${pageNumber}_keys`, keys);
}
Такой подход позволяет извлекать страницу целиком, не выполняя полный перебор всего хранилища.
При извлечении страницы необходимо сначала получить массив ключей страницы, а затем запросить соответствующие значения:
import { get } from 'idb-keyval';
async function getPage(pageNumber) {
const keys = await get(`page_${pageNumber}_keys`);
if (!keys) return [];
const items = [];
for (const key of keys) {
const item = await get(key);
items.push(item);
}
return items;
}
Такой метод минимизирует количество операций и не требует сортировки всех ключей на клиенте.
Для приложений с большим количеством данных часто применяется ленивое добавление страниц. Структура хранения при этом может включать:
pages, содержащий номера страниц и
массивы ключейПример динамического добавления элементов:
import { get, set } from 'idb-keyval';
async function appendItem(item) {
const lastPageNumber = (await get('last_page')) || 1;
const keys = await get(`page_${lastPageNumber}_keys`) || [];
const newKey = `item_${lastPageNumber}_${keys.length}`;
await set(newKey, item);
keys.push(newKey);
// Проверка на размер страницы
if (keys.length > 20) { // например, 20 элементов на страницу
const nextPage = lastPageNumber + 1;
await set(`page_${nextPage}_keys`, []);
await set('last_page', nextPage);
} else {
await set(`page_${lastPageNumber}_keys`, keys);
await set('last_page', lastPageNumber);
}
}
Так как Idb-keyval не гарантирует встроенного порядка,
важно:
Удаление элемента требует обновления массива ключей страницы:
import { get, set, del } from 'idb-keyval';
async function deleteItem(pageNumber, index) {
const keys = await get(`page_${pageNumber}_keys`);
if (!keys || index >= keys.length) return;
const keyToDelete = keys.splice(index, 1)[0];
await del(keyToDelete);
await set(`page_${pageNumber}_keys`, keys);
}
При удалении можно объединять страницы или перераспределять элементы, чтобы поддерживать одинаковый размер страниц и корректное отображение.
Для больших хранилищ следует:
Promise.all при
извлечении нескольких значений для ускорения.Пример батчевого чтения страницы:
import { get } from 'idb-keyval';
async function getPageBatch(pageNumber) {
const keys = await get(`page_${pageNumber}_keys`);
if (!keys) return [];
const promises = keys.map(key => get(key));
return await Promise.all(promises);
}
item_page_index.Такой подход позволяет реализовать полноценную постраничную загрузку
данных в Idb-keyval, обходясь без встроенных индексов и
сохраняя высокую производительность при росте объёма данных.