Головна / Статті / Практичні нотатки: IndexedDB — це не лише зберігання в браузері: як створювати

Практичні нотатки: IndexedDB — це не лише зберігання в браузері: як створювати

Покрокове керівництво з практичних нотаток: IndexedDB — це не лише зберігання даних у браузері: як створювати контракти, перевірки та готові блоки коду для команд, які використовують цю модель.

3095 слів

У цьому посібнику детально описано шлях від сировини до функціональної системи для проекту «IndexedDB – це не лише зберігання в браузері: як створювати веб-додатки, орієнтовані на роботу без підключення до Інтернету». Основна увага приділяється конкретним крокам виконання, чітким перевіркам та коду, який можна просто додати до репозиторію, не намагаючись здогадатися про його призначення. На етапі огляду необхідно визначити вхідні дані, виконавця кроку та критерії завершення перед зміною коду. Виконавці мають мати можливість повторно запустити крок з відомої точки контролю, не намагаючись зрозуміти прихований стан системи. Краще використовувати невеликі, тестирувані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має бути пов’язана з конкретною функцією, а не з складною структурою процесу.

localStorage вже недостатньо

Під час роботи над етапом «localStorage більше не достатньо», спочатку складіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Дайте назви елементам, визначте критерії успіху та не допускайте беззвучного часткового виконання. Вимірюйте рівень запам’ятовування на фіксованому наборі запитань перед налаштуванням запрошень. Часта зміна запрошень рідко виправляє слабкі алгоритми пошуку.

localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");
localStorage.setItem(
  "user",
  JSON.stringify(user)
);
const user = JSON.parse(
  localStorage.getItem("user")
);
50,000 products
10,000 orders
Thousands of customer records
Offline mutations
Synchronization metadata

IndexedDB — це база даних усередині браузера

Під час роботи над етапом «IndexedDB — це база даних» спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат на ранньому етапі запобігає несподіваним рахункам, коли система переходить від демо-режиму до спільних середовищ. Вимірюйте ефективність пошуку на фіксованому наборі запитань перед налаштуванням формулювань запитів. Часта зміна формулювань рідко допомагає покращити якість пошуку.

Web Application
       │
       ▼
   IndexedDB
       │
       ├── Users
       ├── Products
       ├── Orders
       ├── Messages
       └── Pending Sync

Основні концепції

Під час роботи над етапом «Основні концепції» спочатку запишіть умови взаємодії: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Вимірюйте рівень відтворення інформації за фіксованим набором запитань перед налаштуванням підказок. Зміна підказок рідко вирішує проблеми слабкого пошуку інформації. Під час роботи над етапом «Основні концепції» спочатку запишіть умови взаємодії: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

База даних

Етап бази даних працює найкраще, коли його розглядають як вимірювану поверхню. Зафіксуйте один ідеальний зразок даних, один випадок невдачі та примітку щодо скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Розділіть політику часткового оброблення даних від політики їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.

my-app-db

Об’єктний сховище

Етап Object Store працює найкраще, коли його розглядають як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу завдань. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Відомі заздалегідь витрати запобігають несподіваним рахункам під час переходу від демо-середовища до спільних. Розділяйте політику часткового оброблення даних та політику їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.

users
orders
products

Ключові моменти

Етап Key працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Розділяйте політику часткового оброблення даних та політику їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості. Етап Key працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

user_123
order_456

Індекс

На етапі індексації необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви елементів, визначте критерії успіху та не допускайте мовчазного часткового завершення. Наводьте уривки тексту, які фактично лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинації від прогалин у процесі індексації.

orders
  ├── id
  ├── customerId
  ├── status
  └── createdAt
customerId

Транзакція

На етапі обробки транзакції необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке відображення витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні. Наводьте конкретні уривки тексту, які лягли в основу відповіді. Без посилань оператори не зможуть відрізнити галюцинації від проблем з індексуванням.

Простий приклад

На етапі «Простий приклад» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь алгоритм. Необхідно наводити конкретні уривки тексту, які лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинацію від проблем із індексуванням. На етапі «Простий приклад» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру обробки даних.

const request = indexedDB.open("MyAppDB", 1);
request.onupgradeneeded = () => {
  const db = request.result;  db.createObjectStore("users", {
    keyPath: "id"
  });
};request.onsuccess = () => {
  const db = request.result;  console.log("Database opened");
};
const transaction = db.transaction(
  "users",
  "readwrite"
);
const store = transaction.objectStore("users");store.put({
  id: "user_123",
  name: "Alex",
  email: "alex@example.com"
});
const transaction = db.transaction(
  "users",
  "readonly"
);
const store = transaction.objectStore("users");const request = store.get("user_123");request.onsuccess = () => {
  console.log(request.result);
};

Чому індекси мають значення

Під час роботи над етапом «Чому індекси мають значення» спочатку запишіть угоду: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Позначте елементи, визначте критерії успіху та не допускайте беззвучного часткового виконання. Вимірюйте рівень відтворення інформації за фіксованим набором запитань перед налаштуванням запрошень. Часті зміни запрошень рідко виправляють слабкі алгоритми пошуку.

100,000 orders
All orders for customer_123
orders
   │
   ├── Primary Key: id
   │
   └── Index: customerId
customerId = customer_123
        │
        ▼
      Index
        │
        ▼
 matching orders

Транзакції важливіші, ніж здається

Під час роботи над етапом «Транзакції мають вищий пріоритет» спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та наслідки часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних. Вимірюйте ефективність пошуку на фіксованому наборі запитань перед налаштуванням формулювань запитів. Часта зміна формулювань рідко допомагає покращити якість пошуку.

Transaction
   │
   ├── Create Order
   ├── Create Order Items
   └── Create Sync Job
          │
          ▼
       COMMIT
ROLLBACK

IndexedDB дозволяє архітектуру, орієнтовану на роботу в автономному режимі

Під час роботи над етапом «IndexedDB – архітектура з офлайн-приоритетом», спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Вимірюйте рівень відтворення інформації за фіксованим набором запитань перед налаштуванням підказок. Зміни підказок рідко виправляють слабкі алгоритми пошуку. Під час роботи над етапом «IndexedDB – архітектура з офлайн-приоритетом», спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Віддавайте перевагу малим, тестованим одиницям коду перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

No Internet
    ↓
Application stops working
             ┌──────────────┐
             │ Web App      │
             └──────┬───────┘
                    │
             ┌──────▼───────┐
             │  IndexedDB   │
             └──────┬───────┘
                    │
             ┌──────▼───────┐
             │ Sync Engine  │
             └──────┬───────┘
                    │
              Internet?
                 /    \
               No      Yes
               │        │
               ▼        ▼
           Stay       API
           Local        │
                        ▼
                     Server

Шаблон вихідної скриньки у браузері

Шаблон вихідної скриньки на цьому етапі працює найкраще, коли його розглядають як вимірювану поверхню. Зафіксуйте один ідеальний зразок, один випадок невдачі та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Розділіть політику часткового оброблення даних від політики їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості.

orders
outbox
{
  id: "event_123",
  type: "ORDER_CREATED",
  aggregateId: "order_456",
  payload: {...},
  status: "pending",
  createdAt: 1724930000
}
IndexedDB
    │
    ▼
Pending Outbox Events
    │
    ▼
Sync Worker
    │
    ▼
API
    │
    ▼
Server
pending
   ↓
synced

Але офлайн-синхронізація створює нові проблеми

Офлайн-синхронізація працює найкраще, якщо її розглядати як вимірювану поверхню. Запишіть один ідеальний варіант транскрипції, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ. Розділяйте політику часткової обробки даних та політику їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.

Name = "John"
Name = "Jonathan"

Останній внесок — найкращий

Етап «Останній запис перемагає» функціонує найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Розділяйте політику поділу на частини та політику отримання даних. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості. Етап «Останній запис перемагає» функціонує найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Віддавайте перевагу малим, тестованим одиницям перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

Перемога сервера

Для етапу «Перемога сервера» необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте беззвучного часткового завершення. Наводьте конкретні уривки, які лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинацію від проблем із індексуванням.

Перемога клієнта

На етапі Client Wins необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке відображення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних. Наводьте конкретні уривки тексту, які лягли в основу відповіді. Без посилань оператори не зможуть відрізнити галюцинації від проблем з індексуванням.

Об’єднання на рівні полів

На етапі злиття на рівні полів необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевірити, не читаючи весь алгоритм. Наводьте ті уривки, які фактично лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинацію від проблем із індексуванням. На етапі злиття на рівні полів необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Віддавайте перевагу невеликим, тестованим одиницям перед об’ємними скриптами. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану систему обробки даних.

Вирішення конфліктів

Під час роботи над етапом вирішення конфліктів спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте беззвучного часткового виконання завдань. Перед налаштуванням запитів вимірюйте рівень точності на фіксованому наборі запитань. Часта зміна формулювань запитів рідко допомагає покращити якість пошуку.

Ідемпотентність також має значення тут

Під час роботи над етапом «Ідемпотентність також має значення» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени чи запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних. Вимірюйте рівень точності відповідей на фіксований набір запитань перед налаштуванням промптів. Часта зміна промптів рідко допомагає покращити якість пошуку.

ORDER_CREATED
Request 1 → SUCCESS
Request 2 → SUCCESS
clientMutationId = "mutation_123"
UNIQUE(clientMutationId)

Не вважайте IndexedDB своєю серверною базою даних

Під час роботи над етапом «Не обробляйте IndexedDB» спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретів та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Вимірюйте рівень відтворення даних на фіксованому наборі запитань перед налаштуванням підказок. Зміни підказок рідко виправляють слабкі алгоритми пошуку. Під час роботи над етапом «Не обробляйте IndexedDB» спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Віддавайте перевагу малим, тестованим одиницям коду перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

               Server
                 │
             PostgreSQL
                 │
                 ▼
                API
                 ▲
                 │
             Sync Layer
                 ▲
                 │
              IndexedDB
                 ▲
                 │
              Web App

Що варто зберігати?

Етап «Що слід зберігати» функціонує найкраще, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний зразок результату, один випадок невдачі та примітку щодо скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Розділіть політику часткового оброблення даних від політики їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.

Products
Recent Orders
Customer Data
Drafts
User Preferences
Offline Mutations
Sync Metadata

Зберігання не є безобмежним

Етап «Зберігання не є необмеженим» найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу завдань. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуальний контроль витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних. Розділяйте політику часткового оброблення даних та політику їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості.

IndexedDB за замовчуванням не є кешем

IndexedDB Is Not a stage найкраще функціонує, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний зразок роботи, один випадок збою та примітку щодо скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища секретних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Розділяйте політику часткового оброблення даних та політику їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості. IndexedDB Is Not a stage найкраще функціонує, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний зразок роботи, один випадок збою та примітку щодо скасування змін перед розширенням обсягу. Віддавайте перевагу малим, тестованим одиницям коду перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій.

Практична архітектура

На етапі «Практична архітектура» необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання. Наводьте конкретні уривки, які лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинації від проблем з індексуванням.

                 ┌───────────────┐
                 │   Web Client  │
                 └───────┬───────┘
                         │
              ┌──────────▼──────────┐
              │ Application State   │
              └──────────┬──────────┘
                         │
              ┌──────────▼──────────┐
              │     IndexedDB       │
              │                     │
              │ Products            │
              │ Orders              │
              │ Drafts              │
              │ Outbox              │
              │ Sync Metadata       │
              └──────────┬──────────┘
                         │
                    Sync Engine
                         │
                ┌────────▼────────┐
                │      API        │
                └────────┬────────┘
                         │
                ┌────────▼────────┐
                │    Database     │
                └─────────────────┘

Поширені помилки

На етапі «Поширені помилки» необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан.

Помилка 1: Використання localStorage для всього

Помилка 2: Розгляд IndexedDB як постійного джерела інформації

Помилка 3: Ігнорування конфліктів

Помилка 4: Забування про дублювання під час синхронізації

Помилка 5: Зберігання всього

Помилка 6: Розробка підтримки офлайн у кінцевому етапі

Важливіший урок

Останні думки

Browser
   ↕
Server

Чек-лист для роботи