IndexedDB создает надежную автономную веб-базу данных

база данных внешний интерфейс JavaScript Открытый исходный код

У Жиху и у меня в повседневной работе часто возникает вопрос:

Передняя часть еще горячая?

Я просто хочу сказать следующее:

Люди, наблюдающие за огнем с другой стороны, никогда не смогут понять причину огня. Только, будучи в шторме, они могут находить глаз ветра - «Цинь Шиминги»

Вы ТМ даже не смотрите на текущую разработку фронтенда, как судить популярен фронтенд или нет, стоит ли пробовать другой контент? Почему я так увлечен новыми технологиями? Основная причина в том, что они боятся быть устраненными неким подрывным содержанием и отстать от пограничного поля. Чтобы выразить это словами:穷,所以只能学了.... Так что эта статья будет анализировать с самого началаIndexedDBРазработка приложения на фронтенде.

indexedDB в настоящее время постепенно набирает популярность и применяется во внешнем интерфейсе. Он приближается к темпам передовых автономных технологий баз данных. Раньше это был манифест, localStorage, куки, потом webSQL, а теперь indexedDB постепенно распознается основными браузерами. Мы также можем разработать для него технологические инновации. Например, сейчас очень популярны небольшие видеоролики, поэтому мы можем кэшировать их через cacheStorage при просмотре пользователями, а затем использовать технологию WebRTC для управления P2P-раздачей, однако нужно обратить внимание на разумное использование размера, иначе последствия будут действительно серьезно.

Общая архитектура indexedDB состоит из ряда отдельных концепций, все из которых перечислены ниже. На первый взгляд вы обнаружите, что логики нет, но здесь я нарисовал логическую схему, которая будет соединяться последовательно по вызову функции.

  • IDBRequest
  • IDBFactory
  • IDBDatabase
  • IDBObjectStore
  • IDBIndex
  • IDBKeyRange
  • IDBCursor
  • IDBTransaction

Общая логическая схема выглядит следующим образом:

逻辑联系框图

TL;DR

Далее в основном представлены основные концепции indexedDB и практический код в практических приложениях.

  • Основные понятия IndexedDB. В indexedDB общая структура данных разделена в соответствии с индексом индекса.
  • Обновление базы данных indexedDB — очень болезненная вещь, потому что из-за гибкости Сети вам нужно делать как обновление восходящей версии, так и отказоустойчивость нисходящей версии.
  • indexedDB эффективный механизм индексации, внутренне indexedDB уже обеспечиваетindex,cursorВ ожидании эффективного механизма индексации рекомендуется не извлекать все данные напрямую, а затем фильтровать их, а использовать их напрямую.cursorпровести.
  • Наконец, порекомендуйте несколько часто используемых библиотек.

Еще можете обратить внимание на мой паблик-аккаунт: фронтенд маленького Джимми (QR-код внизу статьи)

Автономное хранилище

IndexedDB может хранить множество данных, таких как объекты, файлы, большие двоичные объекты и т. д. Структура хранения внутри хранится в соответствии с базой данных. В каждой БД могут быть разные хранилища объектов. Конкретная структура выглядит следующим образом:

indexedDB 结构图

Также мы можем датьkeyУстановите соответствующее конкретное значение, а затем при индексировании вы сможете получить конкретное содержимое напрямую через ключ. Обратите внимание, что при использовании IndexDB следует тому же принципу домена.

основные понятия indexDB

В indexDB есть несколько основных объектов операций:

  • База данных: ПройтиopenМетод открывается напрямую, и вы можете получить БД экземпляра. На странице может быть создано несколько БД, но обычно одна.
idb.open(name, version, upgradeCallback)
  • Хранилище объектов: это конкретный объект, хранящийся в БД. Это может соответствовать содержимому таблицы в SQL. Его структура хранения:

image.png-392.5kB

  • Индекс: Подобно внешним ссылкам, это своего рода хранилище объектов, которое в основном используется для индексации данных в других хранилищах объектов в хранилище онтологий. Разница в том, что ключ и индекс не совпадают. Вы можете обратиться к:index DEMO,mdn index. Как показано на рисунке ниже:

image.png-59.8kB

Следующий код:

// 创建 index
var myIndex = objectStore.index('lName'); 
  • транзакция: транзакция на самом деле представляет собой набор содержимого CRUD. Если одна из ссылок выходит из строя, вся транзакция отменяется. Например:
var trans1 = db.transaction("foo", "readwrite");
var trans2 = db.transaction("foo", "readwrite");
var objectStore2 = trans2.objectStore("foo")
var objectStore1 = trans1.objectStore("foo")
objectStore2.put("2", "key");
objectStore1.put("1", "key");
  • курсор: в основном используется для просмотра содержимого данных в базе данных. главным образом черезopenCursorконтролировать.
function displayData() {
  var transaction = db.transaction(['rushAlbumList'], "readonly");
  var objectStore = transaction.objectStore('rushAlbumList');

  objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
      var listItem = document.createElement('li');
      listItem.innerHTML = cursor.value.albumTitle + ', ' + cursor.value.year;
      list.appendChild(listItem);  

      cursor.continue();
    } else {
      console.log('Entries all displayed.');
    }
  };
}

Как использовать ИндексБД

Несколько основных концепций были упомянуты выше. Тогда давайте попрактикуемся в IndexDB. На самом деле, чтобы начать работу с IndexDB, нужно сделать несколько основных вещей.

  • Открыть таблицу базы данных
  • Установить указанный первичный ключ
  • Индекс определенного индекса

Очень простой код для создания IndexedDB на ранней стадии выглядит следующим образом:

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // 错误处理程序在这里。
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;
  // 设置 id 为 primaryKey 参数
  var objectStore = db.createObjectStore("customers", { keyPath: "id",{autoIncrement:true} });
  
  // 设置指定索引,并确保唯一性
  objectStore.createIndex("name", "name", { unique: false });
  objectStore.createIndex("email", "email", { unique: true });

};

Вышеупомянутое в основном делает 3 вещи:

  • Открыть таблицу базы данных
  • Создайте новый магазин и установите первичный ключ
  • установить индекс

Открытие таблицы БД это в основном номер версии и название.Тут особо и говорить не о чем.Начнем непосредственно с создания магазина.

Создать хранилище объектов

Используемый метод тот же, что и в IDBDatabase.createObjectStoreметод.

var objectStore = db.createObjectStore("customers", { keyPath: "id",{autoIncrement:true} });

Основная функция строится как:

IDBObjectStore createObjectStore(DOMString name,
                                               optional IDBObjectStoreParameters options)
                                               
dictionary IDBObjectStoreParameters {
  (DOMString or sequence<DOMString>)? keyPath = null;
  boolean autoIncrement = false;
};
  • keyPath: ключ, используемый для установки первичного ключа. Подробнее см. в разделе о разнице между keyPath и генератором ниже.
  • autoIncrement: использовать ли функцию ключа автоинкремента.

Ключ создан главным образом для обеспечения уникальной идентификации при вставке данных.

Однако часто первичный ключ (ключ) не является хорошим способом для завершения индекса, в конкретной практике вам также нужны вторичные ключи (помощь-ключ) для завершения работы вторичных индексов, это будет сопоставлено с IndexDB.index.

установить индексный индекс

После того, как PK (Primary key) создан, для лучшей производительности поиска нам нужно создать дополнительныеindex. Его можно использовать прямо здесь:

objectStore.createIndex('indexName', 'property', options);
  • indexName: установить имя текущего индекса
  • свойство: из сохраненных данных укажите свойство, на которое указывает индекс.

Среди них options имеет три варианта:

  • уникальный: можно ли повторить текущий ключ (наиболее часто используемый)
  • multiEntry: при установке текущего свойства в виде массива будет установлено значение индекса для каждого элемента в массиве.
# 创建一个名字叫 titleIndex 的 index,并且存储的 index 不能重复
DB.createIndex('titleIndex', 'title', {unique: false});

Для получения подробной информации см.:MDN createIndex Propа такжеgoogleDeveloper Index.

Добавить или удалить данные

Чтобы добавить или удалить данные в IndexedDB, вам необходимоtransactionзавершено в. И это добавление и удаление данных можно понимать как однократноеrequest, что эквивалентноtransactionкоторый управляет всеми текущими логическими операциямиrequest. Поэтому, прежде чем официально приступить к манипулированию данными, нам нужно кратко представить, как создать транзакцию.

создание транзакций

transactionAPI, как показано ниже [Код 1]. При создании необходимо вручную указать, какой тип операции является текущей транзакцией.Основное содержание:

  • "только для чтения": только для чтения
  • "readwrite": читать и писать
  • "versionchange": Это нельзя указать вручную и будетupgradeneededОн автоматически создается в событии обратного вызова. Его можно использовать для изменения структурных данных существующего хранилища объектов, таких как индекс и т. д.

Вы можете сделать это, открыв базу данных с помощьюIDBDataBaseВверхtransactionсоздание метода, например [Код 2].

[代码1]
  [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
                                         optional IDBTransactionMode mode = "readonly");
                                         
[代码2]
var transaction = db.transaction(["customers"], "readwrite");
var objectStore = transaction.objectStore("customers");
# 遍历存储数据
for (var i in customerData) {
  var request = objectStore.add(customerData[i]);
  request.onsuccess = function(event) {
    // success, done?
  };
}

Когда транзакция создается, она может не только указать режим выполнения, но и указать область ObjectStore, на которую может повлиять эта транзакция.transactionКоторый является параметром, переданным в данные, а затемobjectStore()Метод открывает несколько ОС для работы, как показано ниже [Код 3].

[代码3]
var tx = db.transaction(["books","person"], "readonly");
var books = tx.objectStore("books");
var person = tx.objectStore("person");

оперативные данные

После завершения создания транзакции мы можем официально приступить к интерактивной работе с данными, то есть написать свою конкретную бизнес-логику. Как следует [Код 1], операция полной транзакции данных.

[代码1]
var tx = db.transaction("books", "readwrite");
var store = tx.objectStore("books");

store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});

tx.oncomplete = function() {
  // All requests have succeeded and the transaction has committed.
};

пройти черезobjectStoreВызвав полученный объект IDBObjectStore, мы можем выполнить добавление, удаление и изменение некоторых столбцов. Вы можете обратиться к [Код 2]. Подробности смотрите в конце статьиappendix.

[代码2]
  [NewObject] IDBRequest put(any value, optional any key);
  [NewObject] IDBRequest add(any value, optional any key);
  [NewObject] IDBRequest delete(any query);

данные индекса

Данные индекса являются одними из самых важных в любой базе данных. Здесь мы можем использовать курсор, индекс, чтобы сделать это. Например, чтобы быстро проиндексировать значение ключа по индексу, обратитесь к [Код 1].

[代码1]
var index = objectStore.index("name");
index.get("Donna").onsuccess = function(event) {
  alert("Donna's SSN is " + event.target.result.ssn);
};

Для получения более подробной информации, пожалуйста, обратитесь к следующемуМетод индексации данных.

keyPath и генератор ключей

Что такое keyPath и keyGenerator, следует рассматривать как относительно сложную концепцию для понимания в IndexedDB. Проще говоря, когда IndexedDB создает хранилище, он должен убедиться, что данные в нем уникальны, поэтому его необходимо настроить так же, как и другие базы данных.primary Keyразличать разные данные. А keyPath и Generator — это два разных способа установки ключей.

установить ключевой путь

# 设置预先需要存放的数据

const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
];

# 通过 keyPath 设置 Primary Key
var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

Поскольку ssn уникален в этом наборе данных, мы можем использовать его какkeyPathгарантияuniqueхарактеристики. В качестве альтернативы можно установить значение ключа с автоматическим приращением, напримерid++похожий.

upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement:true});

использовать генератор

Генератор автоматически создает уникальное значение каждый раз при добавлении данных. Это уникальное значение отделено от ваших фактических данных. непосредственно черезautoIncrement:trueнастроить его.

upgradeDb.createObjectStore('notes', {autoIncrement:true});

Рекомендации по открытию indexDB

Проверьте, поддерживается ли indexDB

if (!('indexedDB' in window)) {
  console.log('This browser doesn\'t support IndexedDB');
  return;
}

Обновление версии: indexDB

При создании экземпляра indexDB вам необходимо вручную указать номер версии. И чаще всего используется

idb.open('test-db7', 2, function(upgradeDb) {})

Это приведет к проблеме. Например, во время онлайн-процесса пользователя A a запросиет новую версию веб-страницы в первый раз и подключается к версии 2. После обновления веб-страницы снова он ударил другой машину, которая не была в Интернете и подключена к старой версии 1. Ошибка. Основная причина:

API indexedDB не позволяет изменять хранилище данных в базе данных в той же версии, и текущая версия БД не может быть связана с версией более ранней версии.

Например, содержимое версии БД, которую вы определили в начале, таково:

# 版本一定义的内容
db.version(1).stores({friends: "++id,name"});

# 版本二修改结构为:
db.version(2).stores({friends: "++id,name,shoeSize"});

Если в это время пользователь сначала открывает версию (1), но позже получает HTML версии (2), появится ошибка.

Ссылаться на:

изменение версии

обновление новой версии

Это очень важный вопрос в IndexDB. Основная причина в том, что

API indexedDB не позволяет изменять хранилище данных в базе данных в той же версии, и текущая версия БД не может быть связана с версией более ранней версии.

Вышеизложенное можно абстрагироваться в вопрос:

Когда вам нужно обновить версию IndexDB?

  1. таблица в базеkeyPathВремя.
  2. Когда вам нужно изменить структуру таблицы базы данных, например, добавить индекс
# 版本 1 的 DB 设计,有一个主键 id 和 index-name
db
.version(1)
.stores({friends: '++id,name'})

# 如果直接想新增一个 key,例如 male,是无法成功的
db
.version(1)
.stores({friends: '++id,name,male'})

# 正确办法是直接修改版本号更新
db
.version(2)
.stores({friends: '++id,name,male'})

Однако, если вы измените номер версии напрямую, будет такой случай:

  • Из-за исходной проблемы с обновлением HTML пользователь сначала посещает страницу A версии 1, а затем обновленную страницу B. На данный момент IndexDB был успешно обновлен до более поздней версии. Однако в следующий раз, когда пользователь откроет старую версию страницы A, а более ранняя версия IndexDB все еще подключена к A, будет сообщено об ошибке, что приведет к сбою доступа.

Решение состоит в том, чтобы установить фильтр, вopen, вручную введите номер версии:

# 打开版本 1 的数据库
var dbPromise = idb.open('db1', 1, function(upgradeDb){...})

# 打开版本 2 的数据库
var dbPromise = idb.open('db2', 2, function(upgradeDb){...})

Однако это вызовет другую проблему, то есть миграцию данных (данные старой версии нельзя не делать). Здесь IndexDB инициирует для вас updateCallback, и вы можете напрямую выполнять в нем соответствующую обработку переноса данных.

var dbPromise = idb.open('test-db7', 2, function(upgradeDb) {
  switch (upgradeDb.oldVersion) {
    case 0:
      upgradeDb.createObjectStore('store', {keyPath: 'name'});
    case 1:
      var peopleStore = upgradeDb.transaction.objectStore('store');
      peopleStore.createIndex('price', 'price');
  }
});

При его использовании необходимо обратить внимание на обработку обновления версии БД, например, в таком случае ваша версия уже 3, а вам нужно обработать данные версии 2:

# 将版本二 中的 name 拆分为  firstName 和 lastName
db.version(3).stores({friends: "++id,shoeSize,firstName,lastName"}).upgrade(function(t) {
    
    return t.friends.toCollection().modify(function(friend) {
        // Modify each friend:
        friend.firstName = friend.name.split(' ')[0];
        friend.lastName = friend.name.split(' ')[1];
        delete friend.name;
    });
});

Это нормально для пользователей, у которых есть база данных версии 2, но для некоторых пользователей, которые не обращались к вашей базе данных, это, несомненно, сообщит об ошибке. Решения:

  • Сохранение для каждой версии, создание полей и магазинов
  • В обратном вызове обновления вы можете судить о том, существуют ли обработанные данные.

В базе данных Dexie.js DB вам необходимо сохранить метод создания каждой БД, что на самом деле делается путем добавления swtich case для обновления каждой версии:

# Dexie.js 保留 DB 数据库
db.version(1).stores({friends: "++id,name"});
db.version(2).stores({friends: "++id,name,shoeSize"});
db.version(3).stores({friends: "++id,shoeSize,firstName,lastName"}).upgrade(...)

# 内部原理,直接添加 switch case 完成版本更新
var dbPromise = idb.open('test-db7', 2, function(upgradeDb) {
  switch (upgradeDb.oldVersion) {
    case 0:
      upgradeDb.createObjectStore('store', {keyPath: 'name'});
    case 1:
      var peopleStore = upgradeDb.transaction.objectStore('store');
      peopleStore.createIndex('price', 'price');
  }
});

Если вы обнаружите, что страница открыта, но другая страница загружает новый код для обновления, вам также необходимо явно закрыть более раннюю версию indexedDB в это время. Конкретный метод работы заключается в мониторингеonversionchangeСобытие, когда версия обновляется, уведомляет текущую БД о закрытии, а затем выполняет операцию обновления на новой странице.

openReq.onupgradeneeded = function(event) {
  // 所有其它数据库都已经被关掉了,直接更新代码
  db.createObjectStore(/* ... */);
  db.onversionchange = function(event) {
    db.close();
  };

}  
  

Наконец, обновление заключается в том, что есть еще несколько заметок:

  • Обновления версии не могут изменить первичный ключ
  • При откате кода обязательно обратите внимание, не обновлялась ли версия. В противном случае вы можете только постепенно обновлять и повторно изменять номер версии для исправления.

Функции шифрования хранилища

Иногда при сохранении мы хотим получить хеш-ключ, сгенерированный строкой строк. Как мы должны реализовать это в Интернете?

Здесь вы можете напрямую использовать то, что было реализовано в ИнтернетеWebCrypto, для достижения вышеуказанных требований мы можем напрямую использовать внутреннюю частьdigestметод. Здесь на MDN уже есть готовый метод, мы можем использовать его напрямую.

Ссылаться на:

WebCrypto шифрование

Значение верхнего предела хранения

Основные ограничения таковы:

браузер ограничение
Chrome Свободное пространство
Firebox Свободное пространство
Safari < 50MB
IE10 < 250MB

Политика выселения такова:

браузер Политика выселения
Chrome Принять политику LRU после того, как в Chrome закончится место
Firebox Политика LRU, когда весь диск заполнен
Safari нет выселения
Edge нет выселения

Ссылаться на:

Значение верхнего предела хранения Обработка верхнего предельного значения хранилища ядра браузера

Метод индексации данных

В дополнение к базовому CRUD в базе данных главным приоритетом является эффективная структура индекса. В indexedDB мы можем индексировать данные тремя способами:

  • фиксированное значение ключа
  • Внешний ключ индекса (индекс)
  • Курсор (курсор)

Индекс фиксированного ключа

IDBObjectStore предоставляет нам прямой доступ кprimaryKeyЧтобы проиндексировать данные, обратитесь к [Код 1], этот метод требует, чтобы мы знали цельkeyсодержание. Конечно, можно иgetAllВсе индексные данные.

[代码1]
  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any query,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any query,
                                    optional [EnforceRange] unsigned long count);

Например, мы получаем определенный фрагмент данных через primaryKey:

db.transaction("customers").objectStore("customers").get("id_card_1118899").onsuccess = function(event) {
    // data is event.target.result.name
};

Также возможно извлекать данные из всего хранилища объектов. Эти сценарии менее полезны и не будут здесь объясняться. Давайте в основном рассмотрим метод индексации index.

индекс индекс

Если вы хотите запросить определенные данные и пройти весь объект напрямую, это займет много времени и производительности. если мы объединимindexДля классификации ключей можно быстро реализовать индекс указанных данных. Здесь мы можем напрямую использовать вышеуказанный IDBObjectStore.index()метод для получения значения указанного индекса. Конкретный метод см. в [Код 1].

[代码1]
 IDBIndex index(DOMString name);

Этот метод напрямую возвращает объект IDBIndex. Вы также можете понимать это как миниатюрное содержимое данных индекса, аналогичное ObjectStore. Далее мы можем использоватьget()способ получения данных указанного индекса, см. [Код 2].

[代码2]
var index = objectStore.index("name");
index.get("Donna").onsuccess = function(event) {
  alert("Donna's SSN is " + event.target.result.ssn);
};

использоватьgetметод независимо от того, является ли ваш индексuniqueвернет только первые данные. Если вы хотите получить несколько данных, вы можете использоватьgetAll(key)сделать это. пройти черезgetAll()Полученная функция обратного вызова, непосредственно черезevent.target.resultВы можете получить соответствующее значение содержимого.

objectStore.getAll().onsuccess = function(event) {
      printf(event.target.result); // Array
    };

кроме как черезgetAll()Помимо получения всех данных, более эффективнымcursorМетод перебирает полученные данные.

Ссылаться на:

экземпляры getAll() и openCursor

индекс курсора

Так называемый курсор, у всех должно быть предварительное впечатление, точно так же, как вещь на нашей физической линейке, которую можно свободно перемещать, чтобы определить содержимое наведенного объекта. В курсоре есть два основных метода:

  • advance(count): переместите текущую позицию курсора вперёд на количество позиций
  • Продолжить (ключ): нижнее положение для перемещения текущего положения курсора указана ключ, если ключ не предоставлен репрезентативное положение.

Например, мы используем курсор для перемещения по определенным данным хранилища объектов.

objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if(cursor) {
        // cursor.key 
        // cursor.value
      cursor.continue();
    } else {
      console.log('Entries all displayed.');
    }
  };

Как правило, курсоры можно использовать для перемещения по двум типам данных: ObjectStore и Index.

  • Object.store: если для этого объекта используется курсор, он будет сохранен в соответствии сprimaryKeyПройдитесь по всем данным, обратите внимание, что здесь не будет дубликатов, т.к.primaryKeyединственный.
  • индекс: если курсор используется на индексе, текущий индекс будет использоваться для обхода, и могут быть повторения.

Есть два способа открыть курсор на объекте IDBObjectStore:

  • openCursor: пройденный объект представляет собой конкретное значение данных, наиболее часто используемый метод
  • openKeyCursor: пройденный объект является значением ключа данных

Здесь мы проходимopenCursorчтобы напрямую открыть индексный набор данных, а затем просмотреть его.

PersonIndex.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    customers.push(cursor.value);
    cursor.continue();
  }
  else {
    alert("Got all customers: " + customers);
  }
};

В курсоре, также предоставленномupdateа такжеdeleteметод, мы можем использовать его для обновления данных, в противном случае мы можем напрямую использовать тот, который предоставляется ObjectStoreputметод.

В курсоре мы также можем ограничить дальность и направление его обхода. В этой настройке мы напрямуюopenCursor()Параметры передаются в метод, а конструктор метода ссылается на [Код 1]. Он может передавать два параметра, первый используется для указания диапазона, второй используется для указанияcursorнаправление движения.

[代码1]
IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");

Если вам нужно установить диапазон курсора, вам нужно использоватьIDBKeyRangeДля этого объекта вы можете обратиться к [Код 2] с помощью шаблона. Объект, на который ссылается ключ в IDBKeyRange, варьируется от пользователя к пользователю. Если для ObjectStore, то для primaryKey, если для Index, то для текущего indexKey

/ 匹配所有在 “Bill” 前面的, 但是不需要包括 "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

Например, здесь мы устанавливаем диапазон индекса для PersonIndex, то есть индекс находится вvillainhrа такжеjimmyVVмежду наборами данных.

# 都包括 villainhr 和 jimmyVV 的数据
var boundKeyRange = IDBKeyRange.bound("villainhr", "jimmyVV", true, true);

 PersonIndex.openCursor(boundKeyRange).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the matches.
    cursor.continue();
  }
};

Если вы также хотите установить направление обхода и исключать ли повторяющиеся данные, вы также можете установить его в соответствии с типом перечисления [Код 2]. Например, в [Код 3] мы меняем направление курсора по умолчанию, чтобы перемещать данные вprev, начиная с конца.

[代码2]
enum IDBCursorDirection {
  "next",
  "nextunique",
  "prev",
  "prevunique"
};

[代码3]
objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.value 
    cursor.continue();
  }
};

Производительность чтения транзакций

Все операции чтения и записи в indexDB основаны наtransactionРежим. Внутри IDBDatabasetransactionследующим образом [Код 1]. Все чтение и письмо можно сравнить сtransactionЗапрос под областью, только когда все запросы будут выполнены, на этот разtransactionвступит в силу, иначе будет выдано исключение или ошибка.transactionУправление процессом всей транзакции будет завершено в соответствии с тремя событиями: ошибка мониторинга, прерывание и завершение, см. [Код 2].

[代码1]
  [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
                                         optional IDBTransactionMode mode = "readonly");

[代码2]
  attribute EventHandler onabort;
  attribute EventHandler oncomplete;
  attribute EventHandler onerror;

Например:

var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("gg");
request.onsuccess = function(event) {
  // delete, done
};

ты сможешьtransactionВручную передать методreadwriteили другие представления транзакцийreadonlyПараметры, указывающие, как вы будете управлять этой транзакцией. Проблемы с производительностью IndexedDB были определены с момента ее первоначального проектирования.

Транзакции с режимом только для чтения могут выполняться одновременно Транзакции с режимом записи должны выполняться согласно очереди

Это означает, что если вы используетеreadwriteрежим, затем следите за тем,readonlyВы должны дождаться завершения транзакции.

Общие навыки

Сгенерировать первичный ключ для id++

Когда указанный primaryKey генерируется, он генерируетсяcreateObjectStoreметод работы. Иногда мы сталкиваемся с тем, что хотим получить ключ напрямую, и он существует в текущем наборе данных, который можно одновременно добавить в опции.keyPathа такжеautoIncrementАтрибуты. Диапазон ключа [1-2^{53}],Ссылаться наразмер ключа генератора ключей

db.createObjectStore('table1', {keyPath: 'id', autoIncrement: true});

рекомендовать

Рекомендации по чтению

Документация indexedDB W3C Начало работы с indexedDB Начало работы с MDN indexedDB

Простая в использовании рекомендация библиотеки

idb: библиотека БД для промисов

Indexed Appendix

  • База данных IndexedDB использует пары клавиш-значения клавиш для хранения данных. Вы можете создать индекс на свойство объекта для быстрой сортировки запроса и списка. .key может сделать двоичные объекты
  • IndexedDB – это транзакционная база данных. API IndexedDB предоставляет индексы, таблицы, курсоры и т. д., но все они должны зависеть от транзакций.
  • API IndexedDB в основном асинхронный.
  • Запросы к базе данных IndexedDB будут содержать атрибуты события onsuccess и onerror.
  • IndexedDB уведомляет пользователя через события DOM, когда результат готов
  • IndexedDB является объектно-ориентированным. indexedDB не является реляционной базой данных, которая использует двумерные таблицы для представления коллекций. Это очень важно и повлияет на то, как вы проектируете и создаете свое приложение.
  • indexedDB не использует язык структурированных запросов (SQL). Он использует указатель (курсор), сгенерированный индексом (index), для завершения операции запроса, чтобы можно было выполнить итерацию по набору результатов.
  • IndexedDB придерживается политики одинакового происхождения

Ограничение и удаление обращений

  • Глобальное многоязычное смешанное хранилище. Поддержка интернационализации не очень хорошая. Нужно справиться с этим самостоятельно.
  • Синхронизация с серверной базой данных. Вы должны написать код синхронизации самостоятельно.
  • исследовать все.

База данных может быть очищена в следующих случаях:

  • Запросы пользователя на очистку данных.
  • Браузер находится в режиме конфиденциальности. Данные будут удалены при окончательном выходе из браузера.
  • Емкость запоминающих устройств, таких как жесткие диски, ограничена.
  • неправильный
  • Неполное изменение.

общая концепция

база данных

  • База данных: обычно содержит одно или несколько хранилищ объектов.Каждая база данных должна содержать следующее:

    • Имя: оно идентифицирует базу данных в конкретном источнике и остается неизменным в течение всего срока службы базы данных.Это имя может быть любым строковым значением (включая пустую строку).
    • Текущая версия (версия). При первом создании базы данных ее версия равна 1, если не указано иное. Каждая база данных может иметь только одну версию в любой момент времени.
  • Хранилище объектов: раздел, используемый для переноса данных. Данные постоянно хранятся в хранилище объектов в виде пар ключ-значение. В ОС для создания ключа можно использоватьkey generatorа такжеkey path.

    • Генератор ключей: Проще говоря, при хранении данных активно генерируется идентификатор ++, чтобы различать каждую запись. В этом случае ключ для хранения данных хранится отдельно от значения, то есть (вне строки).
    • ключевой путь: требует, чтобы пользователь взял на себя инициативу, чтобы установить ключевое содержание сохраненных данных,
    • запрос: Каждая операция чтения и записи может рассматриваться как запрос.
    • транзакция: набор запросов на чтение и запись.
    • index: специальное хранилище объектов, используемое для индексации данных другого хранилища.
  • Конкретный ключ/значение данных

    • ключ: значение этого ключа, которое может быть сгенерировано тремя способами. генератор ключей, путь к ключу, заданное пользователем значение. Кроме того, этот ключ уникален в текущем хранилище объектов. Тип ключа может быть строкой, датой, числом с плавающей запятой и массивом. Однако в более старых версиях обычно поддерживаются только строки или целые числа. (Теперь версия должна быть в порядке)
      • генератор ключей: эквивалентноid++форму для создания значения ключа.
      • ключевой путь: Текущий указанный ключ может быть указан в соответствии с содержимым значения. Внутри могут быть разделители.
      • Указанный ключ: Это необходимость пользователя вручную указывать генерацию.
    • значение: может хранить логическое значение, число, строку, дату, объект, массив, регулярное выражение, неопределенное и нулевое значение. Файлы и объекты больших двоичных объектов теперь также можно сохранять.

Область действия

  • объем: его можно сравнить с объемом транзакции, т. е. порядком, в котором выполняется серия транзакций. С этим положением несколько транзакций чтения могут выполняться одновременно. Но писать можно только в очереди.
  • диапазон ключей: используется для установки содержимого диапазона ключа для извлечения данных.

Ссылаться на:

Нативные концепции IndexedDB

IDBFactory

Это на самом делеindexDBОбъект установлен выше. Основные API следующие:

[Exposed=(Window,Worker)]
interface IDBFactory {
  [NewObject] IDBOpenDBRequest open(DOMString name,
                                    optional [EnforceRange] unsigned long long version);
  [NewObject] IDBOpenDBRequest deleteDatabase(DOMString name);

  short cmp(any first, any second);
};

вы можете напрямую пройтиopenчтобы открыть базу данных. Обратный вызов для прослушивания результата путем возврата объекта Request:

var request = indexedDB.open('AddressBook', 15);
request.onsuccess = function(evt) {...};
request.onerror = function(evt) {...};

Ссылаться на:

IndexDB Factory API

IDBRequest

когда ты проходишьopenПосле обработки метод получит объект обратного вызова Request. Это экземпляр IDBRequest.

[Exposed=(Window,Worker)]
interface IDBRequest : EventTarget {
  readonly attribute any result; // 通过 open 打开过后的 IDBObjectStore 实例 
  readonly attribute DOMException? error;
  readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
  readonly attribute IDBTransaction? transaction;
  readonly attribute IDBRequestReadyState readyState;

  // Event handlers:
  attribute EventHandler onsuccess;
  attribute EventHandler onerror;
};

enum IDBRequestReadyState {
  "pending",
  "done"
};

[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
  // Event handlers:
  attribute EventHandler onblocked;
  attribute EventHandler onupgradeneeded;
};

ты можешь пройтиresultПолучите результат текущей операции базы данных. Если вы открываете обновленный номер версии, вам также нужно отслеживатьonupgradeneededМероприятие для достижения. Наиболее распространенная ошибка соответствует IndexedDB.OpenVER_ERRнеправильная версия. Это указывает на то, что версия базы данных, хранящаяся на диске, выше версии, которую вы пытаетесь открыть.

db.onerror = function(event) {
  // Generic error handler for all errors targeted at this database's
  // requests!
  alert("Database error: " + event.target.errorCode);
};

Поэтому при создании IndexDB также необходимо управлять операцией обновления ее версии, здесь нужно следить за onupgrade, необходимым для достижения этого.

request.onupgradeneeded = function(event) { 
   // 更新对象存储空间和索引 .... 
};

или мы можем напрямую использоватьidbКрошечная библиотека для реализации операций чтения.

  var dbPromise = idb.open('test-db3', 1, function(upgradeDb) {
    if (!upgradeDb.objectStoreNames.contains('people')) {
      upgradeDb.createObjectStore('people', {keyPath: 'email'});
    }
    if (!upgradeDb.objectStoreNames.contains('notes')) {
      upgradeDb.createObjectStore('notes', {autoIncrement: true});
    }
    if (!upgradeDb.objectStoreNames.contains('logs')) {
      upgradeDb.createObjectStore('logs', {keyPath: 'id', autoIncrement: true});
    }
  });

через которыйonupgradeneededEvent.result, полученный обратным вызовом,IDBDatabaseЭкземпляр , часто используемый для установки индекса и вставки данных. Смотри ниже.

Ссылаться на:

IDBRequest API

IDBDatabase

Этот объект часто используется для создания и удаления хранилища объектов и транзакций. Частьonupgradeneededсобытие приобретеноevent.target.resultОбъект:

request.onupgradeneeded = function(event) { 
   // 更新对象存储空间和索引 .... 
   // event.target.result 对象
};

Конкретный контент API выглядит следующим образом:

[Exposed=(Window,Worker)]
interface IDBDatabase : EventTarget {
  readonly attribute DOMString name;
  readonly attribute unsigned long long version;
  readonly attribute DOMStringList objectStoreNames;

  [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>) storeNames,
                                         optional IDBTransactionMode mode = "readonly");
  void close();

  [NewObject] IDBObjectStore createObjectStore(DOMString name,
                                               optional IDBObjectStoreParameters options);
  void deleteObjectStore(DOMString name);

  // Event handlers:
  attribute EventHandler onabort;
  attribute EventHandler onclose;
  attribute EventHandler onerror;
  attribute EventHandler onversionchange;
};

dictionary IDBObjectStoreParameters {
  (DOMString or sequence<DOMString>)? keyPath = null;
  boolean autoIncrement = false;
};

Если он передает метод createObjectStore, то вы получаетеIDBObjectStoreобъект экземпляра. Если это метод транзакции, то этоIDBTransactionобъект.

IDBObjectStore

Этот объект обычно используется для создания индексов и вставки данных.

Вы можете обратиться к:

[Exposed=(Window,Worker)]
interface IDBObjectStore {
  attribute DOMString name;
  readonly attribute any keyPath;
  readonly attribute DOMStringList indexNames;
  [SameObject] readonly attribute IDBTransaction transaction;
  readonly attribute boolean autoIncrement;

  [NewObject] IDBRequest put(any value, optional any key);
  [NewObject] IDBRequest add(any value, optional any key);
  [NewObject] IDBRequest delete(any query);
  [NewObject] IDBRequest clear();
  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any query,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any query,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");

  IDBIndex index(DOMString name);

  [NewObject] IDBIndex createIndex(DOMString name,
                                   (DOMString or sequence<DOMString>) keyPath,
                                   optional IDBIndexParameters options);
  void deleteIndex(DOMString name);
};

dictionary IDBIndexParameters {
  boolean unique = false;
  boolean multiEntry = false;
};

IDBIndex

Этот объект является объектом операции, используемым для индексации индекса, и он также будет существовать в нем.getа такжеgetAllи другие методы. Подробности следующие:

[Exposed=(Window,Worker)]
interface IDBIndex {
  attribute DOMString name;
  [SameObject] readonly attribute IDBObjectStore objectStore;
  readonly attribute any keyPath;
  readonly attribute boolean multiEntry;
  readonly attribute boolean unique;

  [NewObject] IDBRequest get(any query);
  [NewObject] IDBRequest getKey(any query);
  [NewObject] IDBRequest getAll(optional any query,
                                optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest getAllKeys(optional any query,
                                    optional [EnforceRange] unsigned long count);
  [NewObject] IDBRequest count(optional any query);

  [NewObject] IDBRequest openCursor(optional any query,
                                    optional IDBCursorDirection direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");
};

Ссылаться на:

библиотека с открытым исходным кодом idb, крошечная кодовая база библиотека с открытым исходным кодом treo библиотека с открытым исходным кодом dexie.js indexeddb Нативные концепции IndexedDB