This is the 7th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.

preface

The Indexed Database API, short for IndexedDB, is a solution for storing structured data in the browser. IndexedDB is used to replace the currently deprecated Web SQL Database API. The idea behind IndexedDB was to create an API that would facilitate the storage and retrieval of JavaScript objects, while also supporting queries and searches.

IndexedDB is designed to be almost entirely asynchronous. To this end, most operations are performed in the form of requests that are executed asynchronously, producing successful results or errors. Most IndexedDB operations require the addition of onError and ONSuccess event handlers to determine the output.

In 2017, new major browsers (Chrome, Firefox, Opera, Safari) released full support for IndexedDB. IndexedDB is partially supported by IE10/11 and Edge browsers.

The characteristics of

  • 1 key-value pair storage. IndexedDB uses an object Store internally to store data. All types of data can be stored directly, including JavaScript objects. In the object warehouse, data is stored as “key-value pairs”. Each data record has a corresponding primary key. The primary key is unique and cannot be duplicated, otherwise an error will be thrown.

  • 2 the asynchronous. IndexedDB does not lock the browser and users can still perform other operations, in contrast to LocalStorage, where operations are synchronous. Asynchronous design is designed to prevent massive data reads and writes from slowing down the performance of a web page.

  • 3 Support transactions. IndexedDB supports transaction, which means that if one of a series of steps fails, the entire transaction is cancelled and the database is rolled back to the state before the transaction occurred, without overwriting only a portion of the data.

  • 4 Origin restriction The IndexedDB is subject to the same origin restriction. Each database corresponds to the domain name that created it. Web pages can only access databases under their own domain names, but not cross-domain databases.

  • 5 large storage space IndexedDB has a much larger storage space than LocalStorage, usually no less than 250MB, or even no upper limit.

  • 6 Supports binary storage. IndexedDB can store not only strings but also binary data (ArrayBuffer objects and Blob objects).

The database

IndexedDB is a Database similar to MySQL or Web SQL Database. The biggest difference with traditional databases is that IndexedDB uses object stores instead of tables to hold data. An IndexedDB database is a set of objects stored in a common namespace, similar to a NoSQL-style implementation.

The first step in using an IndexedDB database is to call the indexeddb.open () method and pass it the name of the database to open. If the database with the given name already exists, a request is sent to open it; If not, a request is sent to create and open the database. This method returns an instance of IDBRequest on which you can add onError and onSuccess event handlers

let db,
 request,
 version = 1;
request = indexedDB.open("admin", version);
request.onerror = (event) =>
 alert(`Failed to open: ${event.target.errorCode}`);
request.onsuccess = (event) => {
 db = event.target.result;
}; 
Copy the code

In both event handlers, event.target points to Request, so use either. If the onSuccess event handler is called, an IDBDatabase instance can be accessed through event.target.result, which is stored in a DB variable. After that, all database-related operations are done through the DB objects themselves. If an error occurs during opening the database, an errorCode indicating the problem is stored in event.target.errorCode.

Object storage

Once the database connection is established, the next step is to use object storage. If the database version is not what you expect, you may need to create an object store. However, before you create an object store, it’s worth thinking about what kind of data you want to store.

Suppose you want to store user records that contain user names, passwords, and so on. A record can be represented with the following object:

let user = {
 username: "007",
 firstName: "James",
 lastName: "Bond",
 password: "foo"
}; 
Copy the code

Looking at this object, it’s easy to see which username property is best suited as an object storage key. The username must be globally unique, and it is also the credential that most of the time accesses the data. This key is important because you must specify a key when creating the object store.

The version of the database determines the database schema, including the object stores in the database and the structure of those object stores. If the database does not already exist, the open() operation creates a new database and then triggers upgradenneeded event. You can set up the handler for this event and create the database schema in the handler. If the database exists and you specify a upgradeneeded version number, it automatically triggers upgradenneeded event so that the database schema can be updated in the event handler.

const db = event.target.result; // If so, delete the current objectStore. Test of time can do so / / but this will delete existing data when executing each event handler if (db) objectStoreNames) the contains (" users ")) {db. DeleteObjectStore (" users "); } db.createObjectStore("users", { keyPath: "username" }); };Copy the code

Here the keyPath property of the second parameter represents the property name of the storage object that should be used as the key.

The transaction

Once the object store is created, all the rest is done through transactions. Transactions are created by calling the transaction() method of the database object. Any time you want to read or modify data, you organize all changes through transactions,

Note that request. onSuccess and Request. onupgradeneneeded are both asynchronous, so the required add, delete, alter, and log page needs to be run in asynchronous mode otherwise it will not find db. Transaction

let db; var request = indexedDB.open('users'); Request. Onerror = function (event) {console.log(' database open error '); }; request.onsuccess = function (event) { db = event.target.result; }; request.onupgradeneeded = function(event) { db = event.target.result; var objectStore; if (! db.objectStoreNames.contains('person')) { objectStore = db.createObjectStore('person', { keyPath: 'id' }); }else{ objectStore = db.createObjectStore('person', { keyPath: 'id' }); } objectStore.createIndex('name', 'name', { unique: false }); objectStore.createIndex('age', 'age', { unique: false }); };Copy the code

The write operation

function addData() { console.log(db) var request = db.transaction(['person'], ObjectStore ('person').add({id: 1, name: 'iwhao', age: 18}); Request. Onsuccess = function (event) {console.log(' data was written successfully '); }; Request. onerror = function (event) {console.log(' failed to write data '); }}Copy the code

Read operation

function read() { var transaction = db.transaction(['person']); var objectStore = transaction.objectStore('person'); var request = objectStore.get(1); Request. onerror = function(event) {console.log(' transaction failed '); }; request.onsuccess = function( event) { if (request.result) { console.log(request.result); } else {console.log(' no data record '); }}; }Copy the code

update

function upData() { console.log(db) var request = db.transaction(['person'], ObjectStore ('person'). Put ({id: 1, name: 'whao', age: 20}); Request. Onsuccess = function (event) {console.log(' data was written successfully '); }; Request. onerror = function (event) {console.log(' failed to write data '); }}Copy the code

delete

function del(){ var request = db.transaction(['person'], 'readwrite') .objectStore('person') .delete(1); Request. Onsuccess = function (event) {console.log(' data deleted successfully '); }; }Copy the code

The index

For some data sets, you may need to specify multiple keys for the object store. For example, if both a user ID and a user name are recorded, you may need to retrieve user data either way. To do this, consider using the user ID as the primary key and then creating an index on the user name.

Assume that when creating a new table, the name field is indexed.

objectStore.createIndex('name', 'name', { unique: false });

Copy the code

The first argument to createIndex() is the name of the index, the second argument is the name of the index property, and the third argument is the Options object containing the key unique. Unique in this option should be specified to indicate whether the key is unique among all records. Since username may not be repeated, this key is unique.

Indexing query

function getIndexes() { const transaction = db.transaction(['person']); const store = transaction.objectStore('person'); const index = store.index("name") const request = index.get("iwhao"); Request. onerror = function(event) {console.log(' transaction failed '); }; request.onsuccess = function( event) { if (request.result) { console.log(request.result); } else {console.log(' no data record '); }}; }Copy the code

limit

IndexedDB has many of the same limitations as Web Storage. First, the IndexedDB database is bound to page sources (protocols, domains, and ports), so information cannot be shared across domains. For example, www.iwhao.top and iwhao-simple.iwhao.top correspond to different data stores.

Each source has a limit on how much space it can store. The current limit for Firefox is 50MB per source, while Chrome is 5MB. Firefox Mobile has a 5MB limit and will ask permission if you exceed your quota. Another limitation in Firefox is that local text cannot access the IndexedDB database. Chrome doesn’t have that limitation