Модель реактивных запросов в Dexie строится вокруг идеи автоматического отслеживания зависимостей между чтением данных и их изменениями в хранилище IndexedDB. В отличие от классического подхода, где результат запроса является статичным снимком состояния базы на момент выполнения, реактивная модель предполагает постоянную актуализацию результата при любых изменениях затронутых данных.
В основе механизма лежит анализ того, какие таблицы и индексы были
затронуты во время выполнения запроса. Каждый запрос, выполненный в
реактивном контексте (например, через liveQuery),
регистрирует:
get, where,
toArray, count и др.).Эти данные формируют граф зависимостей запроса, который затем используется для определения необходимости пересчёта результата.
Ключевой принцип заключается в том, что Dexie не отслеживает изменения на уровне отдельных полей объектов напрямую. Вместо этого фиксируется факт потенциального влияния изменения на набор данных, участвующих в запросе.
Запрос становится реактивным только внутри специального контекста выполнения. В этом режиме Dexie временно переключает внутренний трекер зависимостей, который:
В этот момент формируется подписка вида:
Такая структура позволяет минимизировать пересчёты: при изменении данных Dexie может определить, какие именно подписки затронуты.
При выполнении операций модификации данных (put,
add, update, delete) Dexie
инициирует процесс инвалидации зависимых запросов.
Каждая операция проходит через внутренний слой транзакционного контроля, где фиксируются:
Далее происходит сопоставление с зарегистрированными зависимостями. Если хотя бы одна из них пересекается с изменённым диапазоном данных, соответствующий реактивный запрос помечается как устаревший.
После инвалидации Dexie запускает процесс повторного выполнения запроса. Это не мгновенная операция на каждое изменение, а батчируемый процесс:
Псевдопроцесс выглядит следующим образом:
Dexie применяет несколько уровней оптимизации, чтобы избежать лишних перерасчётов.
Сам запрос (его функция) сохраняется, а не только результат. Это позволяет повторно использовать логику без повторного анализа кода.
При возможности Dexie старается определить, можно ли обновить результат частично. Однако в большинстве случаев IndexedDB-запросы являются set-based, поэтому применяется полный пересчёт.
Если в короткий промежуток времени происходит несколько операций записи, они объединяются в одну волну инвалидации. Это снижает количество повторных вычислений.
Если изменение затрагивает таблицу, которая не входит в зависимости запроса, пересчёт не запускается.
Зависимости в Dexie можно условно разделить на несколько категорий:
Самый базовый уровень. Запрос зависит от конкретной таблицы:
count, toArray).Любое изменение в таблице приводит к потенциальной инвалидации.
Если запрос использует where('field') или диапазоны
индексов, зависимость становится более точной.
Например:
db.users.where('age').between(18, 30)
В этом случае пересчёт запускается только при изменениях,
затрагивающих индекс age.
При использовании get(primaryKey) или прямого доступа по
ключу формируется точечная зависимость. Это наиболее эффективный тип
наблюдения, так как затрагивается только один объект.
Одной из ключевых задач системы зависимостей является определение пересечения диапазонов.
Если запрос использует диапазон:
[10, 20]а изменение затрагивает ключ:
15то пересечение считается положительным, и запрос инвалидируется.
При индексных запросах используется аналогичная логика сравнения диапазонов значений.
Внутренне это реализуется через абстракцию диапазонных интервалов, где каждая зависимость хранит:
Пересчёт запросов выполняется асинхронно, что позволяет избежать блокировки основного потока. Dexie использует очередь задач, в которой:
Это особенно важно при высокой частоте обновлений, когда последовательные изменения могут быстро устаревать до завершения вычисления.
Если в процессе пересчёта происходят новые изменения данных, Dexie применяет стратегию:
Таким образом предотвращается ситуация, при которой подписчики получают неконсистентное состояние.
Для предотвращения «дребезга» обновлений Dexie сравнивает предыдущий и новый результат запроса.
Если данные не изменились на уровне структуры и значений, уведомление подписчиков не происходит. Сравнение может включать:
Механизм зависимостей тесно связан с liveQuery, который
является высокоуровневым API реактивности. При использовании
liveQuery:
Это позволяет строить реактивные цепочки, где изменение данных в IndexedDB автоматически приводит к обновлению UI или других слоёв приложения.
Несмотря на гибкость, модель имеет ряд ограничений:
Эти ограничения компенсируются простотой и предсказуемостью модели: система всегда гарантирует консистентность результата ценой возможных повторных вычислений.
В случае составных запросов, включающих несколько условий
(and, or, chained queries), Dexie объединяет
зависимости всех подзапросов.
Примерно это выглядит как объединённый граф:
Любое изменение в любой из этих точек может привести к пересчёту всего составного запроса.
Особое внимание уделяется транзакциям. Изменения внутри одной транзакции:
Это позволяет избежать промежуточных состояний, когда данные находятся в частично изменённом виде.
Система зависимостей и перевычисления запросов в Dexie представляет собой комбинацию:
Такая архитектура обеспечивает предсказуемую реактивность без необходимости ручного управления подписками или сложного кэширования состояния.