preface

People may have the impression that only those on the back end have access to databases. In fact, there are databases in the field of the front end, but may use less, because there are a lot of front-end storage solutions, such as cookies, sessionStorage and so on.

There are two kinds of databases on the browser: webSQL and IndexedDB. However, if Indexed DB database is used in browser, webSQL is almost obsolete. You can check the specific reasons by yourself. Today, WE will mainly discuss the use of Indexed DB database.

1. With IndexedDB profile

MDN’s official website explains Indexed DB as follows:

IndexedDB is an underlying API for storing large amounts of structured data (also file/binary Large objects (BloBs)) on the client side. The API uses indexes for high-performance searches of data. While Web Storage is useful for storing smaller amounts of data, it is inadequate for storing larger amounts of structured data. IndexedDB provides a solution to this scenario.

IndexedDB is primarily used to store large amounts of data on the client side. Cookies, localstorage, and other storage methods have storage size limits. IndexedDB can be used when there is a large amount of data that needs to be stored on the client side.

Comparison of storage modes on clients:

2.IndexedDB usage scenarios

All scenarios assume that the client needs to store a large amount of data:

  1. Data visualization and other interfaces, a large amount of data, each request will consume a lot of performance.
  2. Instant messaging tool, a large number of messages need to be stored locally.
  3. If the capacity of other storage methods is insufficient, use IndexedDB

3. With IndexedDB characteristics

(1) Non-relational database (NoSql)

As we all know, MySQL and other databases are relational databases, and their main feature is that data is stored in the form of a two-dimensional table, while Indexed DB is non-relational database and mainly stores data in the form of key-value pairs.

(2) Persistent storage

Data stored in cookies, localStorage, sessionStorage, etc., will be cleared when we are aware of the browser cache. Data stored in IndexedDB will not be cleared unless the database is manually deleted.

(3) Asynchronous operation

IndexedDB does not lock the browser and users can still perform other operations, in contrast to localStorage, which is synchronous.

(4) Support transactions

IndexedDB supports transactions, 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, similar to transactions in databases such as MySQL.

(6) Same-origin policy

IndexedDB also has 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.

(7) Large storage capacity

This is one of the most salient features of IndexedDB, and is the best reason not to use storage like localStorage.

4. Important concepts of IndexedDB

4.1 warehouse objectStore

IndexedDB does not have the concept of a table. Instead, it has the concept of a store, which can be interpreted as a table.

4.2 the index index

In relational databases, there is also the concept of indexes. We can add indexes to corresponding table fields to speed up the search. IndexedDB also contains indexes. When creating a store, we can create an index at the same time. When querying a store, the index can be filtered by the index.

4.3 the cursor cursor

Cursor is new concept with IndexedDB database, you can imagine the cursor as a pointer, for example, we want to query to satisfy a certain condition, when all the data you need to use a cursor, we let the cursor line go down, the cursor went to the place will return to this line of data, at this time we can visit data judgment, whether meet the conditions.

[Note] : IndexedDB is not as convenient as MySQL, it can only be queried by primary key, index, cursor.

4.4 transactions

IndexedDB supports transactions, that is, any failed operation on the database is rolled back to its original state to ensure data consistency.

5. With IndexedDB field

All IndexedDB operations against the repository are transaction-based.

5.1 Creating or Connecting a Database

The code is as follows:

/** * Open database * @param {object} dbName Database name * @param {string} storeName Warehouse name * @param {string} version Database version * @return */ function openDB(dbName, version = 1) {return new Promise((resolve, resolve)) Reject) = > {/ / browser compatibility var with indexedDB = window. With indexedDB | | window. MozIndexedDB | | window. WebkitIndexedDB | | window.msIndexedDB; let db; // Open the database, if no const request = indexeddb.open (dbName, version); Request. Onsuccess = function (event) {db = event.target.result; // Database object console.log(" database opened successfully "); resolve(db); }; Onerror = function (event) {console.log(" database failed to open "); }; Onupgradenhui required = function (event) {console.log(" onupgradenhui ") is triggered when a database is created or upgraded; db = event.target.result; Var objectStore; ObjectStore = db. CreateObjectStore ("signalChat", {keyPath: "sequenceId", // this is the primary key // autoIncrement: True // implement increment}); CreateIndex ("link", "link", {unique: false}); // createIndex(objectstore.createindex); objectStore.createIndex("sequenceId", "sequenceId", { unique: false }); objectStore.createIndex("messageType", "messageType", { unique: false, }); }; }); }Copy the code

We encapsulate the creation of the database into a function that returns a Promise object that can be chained when called. The function takes two parameters: the database name and the database version. There are three main callback functions inside the function:

  • Onsuccess: callback after the database is opened or created successfully, where we return the database instance.
  • Onerror: Callback after database opening or creation failure.
  • Onupgradenneeded: This function is required to run whenever the database version changes, for example if we want to create a new repository (table) it needs to be used to update the database version.

5.2 Inserting Data

@param {string} storeName = @param {string} data = @param {string} data = @param {string}  storeName, data) { var request = db .transaction([storeName], "Readwrite ") // the transaction object specifies the table name and mode of operation ("read only" or "read/write"). ObjectStore (storeName) // warehouse object.add (data); Request. Onsuccess = function (event) {console.log(" data was written successfully "); }; Request. Onerror = function (event) {console.log(" data write failed "); }; }Copy the code

IndexedDB inserts data in a transaction. The method for inserting data in IndexedDB is simple, using the add method provided by IndexedDB. Here we also encapsulate the insert operation as a function, which takes three parameters as follows:

  • Db: The db instance returned when a database is created or connected, which needs to be saved at that time.
  • StoreName: The warehouse name (or table name) that we created when we created or connected to the database.
  • Data: Data to be inserted, usually an object

** the data to be inserted is an object and must contain the key/value pairs we declare.

5.3 Reading Data Using primary Keys

The code is as follows:

@param {object} DB database instance * @param {string} storeName Warehouse name * @param {string} key Primary key */ function getDataByKey(db, storeName, key) { return new Promise((resolve, reject) => { var transaction = db.transaction([storeName]); Var objectStore = transaction.objectstore (storeName); Var request = objectStore.get(key); Request. Onerror = function (event) {console.log(" transaction failed "); }; Request. onsuccess = function (event) {console.log(" primary key query result: ", request.result); resolve(request.result); }; }); }Copy the code

The primary key, which is the keyPath we declared when we created the database, can query only one piece of data.

5.4 Querying Data using cursors

The code is as follows:

@param {string} storeName = @param {string} storeName = function cursorGetData(db, storeName) { let list = []; Var store = db.transaction (storeName, "readwrite") // transaction. ObjectStore (storeName); Var request = store.opencursor (); Request. Onsuccess = function (e) {var cursor = e.target.result; If (cursor) {// Must check list.push(cursor.value); cursor.continue(); } else {console.log(" Cursor read data: ", list); }}; }Copy the code

The above function opens a cursor, then reads the data line by line, stores it into an array, and finally gets all the data for the entire warehouse.

5.5 Querying Data Using Indexes

The code is as follows:

@param {object} db database instance * @param {string} storeName warehouse name * @param {string} indexName indexName * @param */ function getDataByIndex(db, storeName, indexName, indexValue) { var store = db.transaction(storeName, "readwrite").objectStore(storeName); var request = store.index(indexName).get(indexValue); Request. onerror = function () {console.log(" transaction failed "); }; request.onsuccess = function (e) { var result = e.target.result; Console. log(" index query result: ", result); }; }Copy the code

The index name is the name of the index we created when we created the warehouse, the key in the key-value pair, which will eventually query all the data that meets the value of the function index we passed in.

5.6 Querying Data Using Indexes and Cursors

In Section 5.4 and 5.5, we found that the data queried by index or cursor alone is partial or all of the data. If we want to query all the data in the index that meets certain conditions, it is impossible to use index or cursor alone. Of course, you can query all the data and then loop through the array to filter out the appropriate data, but this is not the best way to do this. The best way to do this is to combine indexes and cursors.

The code is as follows:

/** * query records by index and cursor * @param {object} db database instance * @param {string} storeName warehouse name * @param {string} indexName indexName * @param */ function cursorGetDataByIndex(db, storeName, indexName, indexValue) {let list = []; var store = db.transaction(storeName, "readwrite").objectStore(storeName); Var Request = Store.index (indexName) // Index.opencurSOR (idBKeyRange.only (indexValue)); Request. Onsuccess = function (e) {var cursor = e.target.result; If (cursor) {// Must check list.push(cursor.value); cursor.continue(); } else {console.log(" cursor index query result: ", list); }}; request.onerror = function (e) {}; }Copy the code

The above function takes four arguments:

  • Db: indicates the database instance
  • StoreName: indicates the name of a warehouse
  • IndexName: indicates the indexName
  • IndexName: indicates the index value

Using indexes and cursors in conjunction with queries, we can query all data objects whose indexes match the values of the function we pass in, rather than one or all of the data.

5.7 Query through index and cursor paging

IndexedDB paging queries are not as simple as MySQL paging queries and do not provide a ready-made API such as Limit, so we need to implement paging ourselves.

The code is as follows:

@param {string} storeName warehouse name @param {string} indexName indexName * @param {string} indexName * @param {string} indexName * @param {string} indexValue indexValue * @param {number} page number * @param {number} pageSize number of queries */ function cursorGetDataByIndexAndPage( db, storeName, indexName, indexValue, page, pageSize ) { let list = []; let counter = 0; // count let advanced = true; Var store = db.transaction(storeName, "readwrite").objectStore(storeName); var store = db.transaction(storeName, "readWrite "). Var Request = Store.index (indexName) // Index.opencurSOR (idBKeyRange.only (indexValue)); Request. Onsuccess = function (e) {var cursor = e.target.result; if (page > 1 && advanced) { advanced = false; cursor.advance((page - 1) * pageSize); // How many returns are skipped; } if (cursor) {// Must check list.push(cursor.value); counter++; if (counter < pageSize) { cursor.continue(); } else {cursor = null; Console. log(" paging query results ", list); }} else {console.log(" paging query results ", list); }}; request.onerror = function (e) {}; }Copy the code

One of the IndexedDB APIS is used here: advance. This function will tell us how many start queries our cursor will skip. If we have 10 data per page and now need to query page 2, then we need to skip the previous 10 data and start the query from 11 data until the counter equals 10, then we close the cursor and end the query.

5.8 Updating Data

IndexedDB updates the data simply by using the put method. Note that if the data does not exist in the database, it will be added by default, otherwise it will be updated. Some people like to use the PUT method for updates and additions, which is also possible.

The code is as follows:

@param {object} db instance * @param {string} storeName storeName * @param {object} data data */ function updateDB(db, storeName, data) { var request = db .transaction([storeName], "Readwrite ") // transaction object.objectStore (storeName) // Warehouse object.put (data); Request. Onsuccess = function () {console.log(" data updated successfully "); }; Request. Onerror = function () {console.log(" data update failed "); }; }Copy the code

The PUT method receives a data object.

5.9 Deleting Data Using Primary Keys

The primary key, which we declared keyPath when we created the database, is unique.

The code is as follows:

@param {object} db instance * @param {string} storeName storeName * @param {object} id primary key */ function deleteDB(db, storeName, id) { var request = db .transaction([storeName], "readwrite") .objectStore(storeName) .delete(id); Request. Onsuccess = function () {console.log(" data deleted successfully "); }; Request. Onerror = function () {console.log(" data deletion failed "); }; }Copy the code

This type of deletion can delete only one data and must be passed the primary key.

5.10 Delete specified data using indexes and cursors

Sometimes we can’t get the primary key, so we can only delete the index value. In this way, we can delete one data (the index value is unique) or all data (the index value is not unique).

The code is as follows:

/** * delete specified data by index and cursor * @param {object} db instance * @param {string} storeName warehouse name * @param {string} indexName indexName * @param {object} indexName */ function cursorDelete(db, storeName, indexName, indexValue) { var store = db.transaction(storeName, "readwrite").objectStore(storeName); Var request = Store.index (indexName) // Index object.opencurSOR (idBKeyrange.only (indexValue)); Request. Onsuccess = function (e) {var cursor = e.target.result; var deleteRequest; if (cursor) { deleteRequest = cursor.delete(); Deleterequest.onerror = function () {console.log(" cursor failed to delete this record "); }; Deleterequest.onsuccess = function () {console.log(" cursor deletes this record successfully "); }; cursor.continue(); }}; request.onerror = function (e) {}; }Copy the code

Note that the idbKeyrange.only () API is used when two values are equal. For details on the API, see MDN.

5.11 Shutting down the Database

When our database operation is complete, it is recommended to close it to save resources.

The code is as follows:

@param {object} db */ function closeDB(db) {db.close(); Console. log(" database closed "); }Copy the code

5.12 Deleting a Database

Finally, we need to delete the library to run, delete the operation is also very simple.

The code is as follows:

@param {object} dbName */ function deleteDBAll(dbName) {console.log(dbName); let deleteRequest = window.indexedDB.deleteDatabase(dbName); Deleterequest. onError = function (event) {console.log(" delete failed "); }; Deleterequest. onsuccess = function (event) {console.log(" delete succeeded "); }; }Copy the code

conclusion

The IndexedDB database is not as complex as we expected. It is easy to get started with the basic concepts of adding, deleting, modifying, and searching.

The author making:

github.com/Hacker233