This is the 13th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

preface

The Indexed Database API, or IndexedDB, is a solution for storing structured data in a browser. IndexedDB is used to replace the deprecated Web SQL Database API. The idea behind IndexedDB was to create a set of apis that made it easy to store and retrieve JavaScript objects, as well as support 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, IndexedDB was fully supported by the major new browsers (Chrome, Firefox, Opera, Safari). Ie10/11 and Edge browsers partially support IndexedDB.

The characteristics of

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

  • 2 the asynchronous. IndexedDB operates without locking the browser and allows users to perform other operations, in contrast to LocalStorage, where operations are synchronized. The asynchronous design is designed to prevent large amounts of data being read and written, slowing down the performance of the web page.

  • 3 Support transactions. IndexedDB supports transactions, which means that if one of the steps fails, the entire transaction is cancelled and the database is rolled back to where it was before the transaction occurred. There is no case where only a portion of the data is overwritten.

  • 4 Same-origin restriction IndexedDB is restricted by the same-origin restriction. Each database corresponds to the domain name for which it was created. A web page can only access databases within its own domain name, but cannot access databases across domains.

  • 5 large storage space the storage space of IndexedDB is much larger than that of LocalStorage. Generally speaking, the storage space of IndexedDB is no less than 250MB and there is 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 store 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 the 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 the 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 you can use either. If the onSuccess event handler is called, the instance of the database (IDBDatabase) can be accessed through event.target.result, and the instance is stored in the DB variable. After that, all database-related operations are done through the DB object itself. If an error occurs during database opening, the errorCode representing 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 as expected, you may need to create an object store. However, before you create an object store, it is important to think about what type 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 by the following objects:

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

Looking at this object, it’s easy to see which attribute username is best suited as the object’s storage key. The user name, which must be globally unique, is also the credential for accessing the data in most cases. This key is important because a key must be specified when the object store is created.

The database version determines the database schema, including the object stores in the database and the structure of those object stores. If the database doesn’t already exist, the open() operation creates a new database which then triggers a 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 an updated version number, it immediately triggers Upgradenneeded events so that the database schema can be updated in event handlers.

const db = event.target.result; // Delete the current objectStore if it exists. 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

The keyPath property of the second argument here represents the property name of the stored object that should be used as the key.

The transaction

After the object store is created, all remaining operations are done through transactions. Transactions are created by calling the transaction() method on the database object. Any time you want to read or modify data, you have to organize all the modifications through transactions,

Note that Request. onSuccess and Request. onneeded are both needed asynchronously, so it needs to be needed in asynchronously, otherwise the db.transaction return needed

let db; var request = indexedDB.open('users'); Request. onError = function (event) {console.log(' database opening 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'], 'readwrite') //readwrite表示有读写权限
      .objectStore('person')
      .add({ id: 1, name: 'iwhao', age: 18}); //新增数据
    request.onsuccess = function (event) {
      console.log('数据写入成功');
    };
    request.onerror = function (event) {
      console.log('数据写入失败');
    }
  }
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 obtained '); }}; }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 more than one key for the object store. For example, if both the user ID and the user name are recorded, you may need to retrieve the 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 you create a 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 with the key unique. The unique in this option should have to be specified, indicating whether the key is unique among all records. Because username might 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 obtained '); }}; }Copy the code

limit

Many of the limitations of IndexedDB are actually the same as Web Storage. First, the IndexedDB database is bound to the page source (protocol, domain, and port), so the information cannot be shared across domains. This means that www.wrox.com and p2p.wrox.com will correspond to different data stores. Second, each source has a limit on how much space it can store. The current Firefox limit is 50MB per source, while Chrome is 5MB. Firefox Mobile has a 5MB limit and will ask the user for permission if you exceed the quota. Firefox also has a restriction — local text cannot access the IndexedDB database. Chrome has no such restriction. So when you run the examples in this book here, use Chrome.