An overview of the

This article introduces the methods and scenarios of using IndexedDB, and answers the frequently asked questions.

At the same time, because the related documents in MDN lack relevant logic, it is not easy to understand. This article organizes content through common data storage and manipulation requirements in projects.

Readers will learn how to use IndexedDB properly in their projects, bring local storage to their applications, and avoid some common problems.

Reason: Developers need permanent storage locally

When we do some large SPA page development, we will need to do some local storage of the data.

When the amount of data is not large, we can use SessionStorage or LocalStorage to store, but when the amount of data is large, or meet certain specifications, we can use the database to store the data.

There are web SQL and IndexedDB databases provided by browsers. IndexedDB is recommended over HTML5’s deprecated Web SQL.

structure

Let’s take a look at the overall structure of IndexedDB.

The DB in IndexedDB is the DB in SQL, the Object Store is a table, and the Item is equal to a record in the table.

Use with IndexedDB

Now, we’ll introduce the operation of the IndexedDB in terms of its structure, to give you an idea of the storage space. We mainly introduce:

  • Database operation
  • Data table operation
  • Data manipulation

Database operation

Create or open a database

The first step in using IndexedDB is to create or open a database. We use the window.indexedDb.open (DBName) API to do this. The following is an example:

const request = window.indexedDB.open('test');

request.onupgradeneeded = function (event) {
    
}

request.onsuccess = function(event) {
	//request === event.target;
}
request.onerror = function(event) {}
Copy the code

When this interface is invoked, a new database is created if the current database does not exist.

When a database connection is established, an IDBOpenDBRequest object is returned.

When the connection is established, the onSuccess event is fired, and the target property of the event parameter is the request object.

Onupgradenneeded events are triggered when a database is created or a version is updated.

Update the database version

The second argument to window.indexeddb. open is the version number. If not specified, the default version number is 1. The following is an example:

const request = window.indexedDB.open('test'.2);
Copy the code

When the schema of the database needs to be updated, the version number needs to be updated. If we specify a version number that is higher than the previous version, onUpgradenneeded events are triggered. Similarly, when the database does not exist, this event is triggered and the version is updated to the top version.

Note that the version number is an Unsigned long long number, which means it can be a very large integer. However, it cannot be a decimal as otherwise it will be converted to the nearest integer and may cause onUpgradenneeded events not to fire (bug).

Storage Space Operations

Creating storage Space

We use createObjectStore to create a storage space. Also, use createIndex to create its index. The following is an example:

var request = window.indexedDB.open('test'.1);

request.onupgradeneeded = function (event) {
    var db = event.target.result;
    var objectStore = db.createObjectStore('table1', {keyPath: 'id'.autoIncrement: true});

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

request.onerror = function (event) {
    alert("Why didn't you allow my web app to use IndexedDB? !");
};
Copy the code

Note: Only inonupgradeneededCreate storage space in the callback function, but not after the database is openedsuccessCallback function.

Create a storage space with createObjectStore. Accept two parameters:

  1. The first parameter, the name of the storage space, is ours abovecustomers.
  2. The second parameter, which specifies the storedkeyPathA value is a property of a storage object that can be used as a key value when obtaining storage space data.autoIncrementSpecifies thekeyWhether the value is incrementing (when the key value is the default integer from 1 to 2^53).

CreateIndex can set an index for the current storage space. It takes three arguments:

  1. The first parameter, the name of the index.
  2. The second parameter specifies which attribute of the stored data to build the index from.
  3. The third property, the options object, in which the propertiesuniqueThe value oftrueIndicates that equal index values are not allowed.

Data manipulation

The transaction

In IndexedDB, we can also use transactions to operate on the database. There are three modes for transactions (constants deprecated) :

  • readOnly. Read-only.
  • readwrite, reading and writing.
  • versionchange, the database version changes.

When we create a transaction, we need to select one of the above modes, if not specified, the default mode is read-only. The following is an example:

const transaction = db.transaction(['customers'].'readwrite');
Copy the code

The first parameter of the transaction function is the storage space to be associated, and the second optional parameter is the transaction mode. Similarly, the onsuccess function is fired onsuccess, and the onerror function is fired on failure.

The operations of a transaction are atomic.

Increase the data

When the storage space is initialized, we can put the data into the storage space. To put data into storage space, call the add method directly, as shown in the following example:

var request = window.indexedDB.open('test'.1);

request.onsuccess = function (event) {
    var db = event.target.result;

    var transaction = db.transaction(['table1'].'readwrite');

    var objectStore = transaction.objectStore('table1');

    var index = objectStore.index('name');

    objectStore.add({name: 'a'.age: 10});
    objectStore.add({name: 'b'.age: 20});
}
Copy the code

Note: The second parameter in the add method, the key value, specifies the keyPath value in the storage space. If the data contains a keyPath value or the value is self-incrementing, this parameter can be omitted.

To find the data

Gets data from a specific value

When we need to get data from storage space, we can do it in the following ways:

var request = window.indexedDB.open('test'.1);

request.onsuccess = function (event) {
    var db = event.target.result;

    var transaction = db.transaction(['table1'].'readwrite');

    var objectStore = transaction.objectStore('table1');

    var request = objectStore.get(1);

    request.onsuccess = function (event) {
        // Do something on request.result!
        console.log(request.result);
    };

    request.onerror = function (event) {
        // Error handling!
    };
}
Copy the code

Get data by cursor

Cursors are used when you need to traverse the entire storage space. The cursor can be used as follows:

var request = window.indexedDB.open('test'.1);

request.onsuccess = function (event) {
    var db = event.target.result;

    var transaction = db.transaction(['table1'].'readwrite');

    var objectStore = transaction.objectStore('table1');

    var request = objectStore.openCursor();

    request.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
            // Use the object.assign method to avoid console printing errors
            console.log(Object.assign(cursor.value)); cursor.continue(); }}; request.onerror =function (event) {
        // Error handling!
    };
}
Copy the code

One cavplay to using cursors is that the onSuccess function is still fired when the cursor traverses the entire storage space and does not find the value of the given condition.

OpenCursor and openKeyCursor take two parameters:

  1. The first parameter, traversal range, specifies the cursor’s access range. The range is obtained by means of an IDBKeyRange parameter.

    The following is an example of the traversal range parameters:

Key === 1
const singleKeyRange = IDBKeyRange.only(1);

// Match key >= 1
const lowerBoundKeyRange = IDBKeyRange.lowerBound(1);

// The matching value key > 1
const lowerBoundOpenKeyRange = IDBKeyRange.lowerBound(1.true);

// The matching value key < 2
const upperBoundOpenKeyRange = IDBKeyRange.upperBound(2.true);

Key >= 1 && key < 2
const boundKeyRange = IDBKeyRange.bound(1.2.false.true);

index.openCursor(boundKeyRange).onsuccess = function(event) {
  const cursor = event.target.result;
  if (cursor) {
    // Do something with the matches.cursor.continue(); }};Copy the code

  1. The second argument, the traversal order, specifies the order in which the cursor traversal takes place and how to handle repeats of the same ID (the keyPath attribute specified field). The range is obtained by specifying a string (constants of IDBCursor are deprecated). Among them:

    • next, retrieve all data from front to back (including duplicate data)
    • prev, retrieve all data from back to front (including duplicate data)
    • nextuniqueRetrieve data from front to back (only the first piece of duplicate data is taken, the index is considered to be repeated, the same below)
    • prevuniqueRetrieve data from back to front (only the first duplicate data is taken)

    The following is an example of the traversal sequence parameters:

var request = window.indexedDB.open('test'.1);

request.onsuccess = function (event) {
    var db = event.target.result;

    var transaction = db.transaction(['table1'].'readwrite');

    var objectStore = transaction.objectStore('table1');

    var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound(1.false);
    var request = objectStore.openCursor(lowerBoundOpenKeyRange, IDBCursor.PREV);

    request.onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
            // Use the object.assign method to avoid console printing errors
            console.log(Object.assign(cursor.value)); cursor.continue(); }}; request.onerror =function (event) {
        // Error handling!
    };
}
Copy the code

Using the index

When we built the database earlier, we created two indexes. Now we can also retrieve data by indexing. It is essentially using the same API as before to get the data, but instead of using the keyPath attribute, it turns it into the attribute specified by the index. The following is an example:

var request = window.indexedDB.open('test'.1);

request.onsuccess = function (event) {
    var db = event.target.result;

    var transaction = db.transaction(['table1'].'readwrite');

    var objectStore = transaction.objectStore('table1');

    var index = objectStore.index('name');

    // The first is the get method
    index.get('a').onsuccess = function (event) {
        console.log(event.target.result);
    }

    // Second, the normal cursor method
    index.openCursor().onsuccess = function (event) {
        console.log('openCursor:', event.target.result.value);
    }

    // The third method, the key cursor method, differs from the second method in that the normal cursor has a value to indicate the fetched data, while the key cursor does not
    index.openKeyCursor().onsuccess = function (event) {
        console.log('openKeyCursor:', event.target.result); }}Copy the code

Modify the data

When we need to modify data in storage space, we can use the following API:

var objectStore = transaction.objectStore("customers");

var request = objectStore.put(data);

request.onsuccess = function (event) {}Copy the code

Note: The put method can not only modify existing data, but also add new data to the storage space.

Delete the data

When we need to delete useless data, we can use the following methods:

var objectStore = transaction.objectStore("customers");

var request = objectStore.delete(name);

request.onsuccess = function (event) {}Copy the code

Exception handling

IndexedDB may fail if the browser does the following:

  • The user clears the browser cache
  • The storage space exceeds the size limit. Procedure

At this point, the error needs to be caught and the user prompted. This chapter is not the focus of this article, so I’ll skip it here.

Extension instructions

Values related to

The data type that the key value can accept

In IndexedDB, key values in a key-value pair can accept one of the following types of values:

  • number
  • data
  • string
  • binary
  • array

For details, see the document here.

The data type that key Path can accept

When a key value is changed to a primary key (keyPath), it can only have one of the following values:

  • Blob
  • File
  • Array
  • String

Note: Spaces cannot appear in key Path.

For details, see the document here.

Value Specifies the data type that can be accepted

In IndexedDB, value can accept all types of ecMA-262 values, such as String, Date, ImageDate, etc.

Issues related to

Will the increment of the key value be affected after the transaction is interrupted

IndexedDB uses an increasing key value when no key value is specified. If a transaction is interrupted in the middle, the increment of the key value will start with the key before the interrupted transaction began.

Safety related

IndexedDB is also limited by the browser’s same origin policy.

The user related

Clear the cache

When the user clears the browser cache, the relevant data in the IndexedDB may be cleared.

Access permissions

When some browsers, such as Safari mobile privacy mode, access IndexedDB, exceptions may occur due to lack of permissions (including LocalStorage), which requires exception handling.

conclusion

IndexedDB plays an irreplaceable role in local storage. It is an alternative to the relational Web SQL database and can store large amounts of data. In many scenarios where offline storage is needed, it can provide effective support.

However, the use of IndexedDB still needs to avoid some possible problems, or to have a certain tolerance to the adverse effects. This will not have a major impact on the application.

reference

  • Same-origin policy of the browser
  • Getting started with indexedDB MDN
  • With IndexedDB API reference
  • W3C IndexedDB 2.0 specification