Un guide rapide mais complet sur IndexedDB et le stockage des données dans les navigateurs

Vous souhaitez apprendre JavaScript? Obtenez mon ebook JavaScript sur jshandbook.com

Introduction à IndexedDB

IndexedDB est l'une des capacités de stockage introduites dans les navigateurs au fil des ans.

C'est un magasin clé / valeur (une base de données noSQL) considéré comme la solution définitive pour stocker des données dans les navigateurs .

C'est une API asynchrone, ce qui signifie que l'exécution d'opérations coûteuses ne bloquera pas le thread d'interface utilisateur, offrant une expérience bâclée aux utilisateurs. Il peut stocker une quantité indéfinie de données, bien qu'une fois au-dessus d'un certain seuil, l'utilisateur soit invité à donner au site des limites plus élevées.

Il est pris en charge sur tous les navigateurs modernes.

Il prend en charge les transactions, la gestion des versions et donne de bonnes performances.

Dans le navigateur, nous pouvons également utiliser:

  • Cookies : peuvent héberger une très petite quantité de chaînes
  • Stockage Web (ou stockage DOM), un terme qui identifie généralement localStorage et sessionStorage, deux magasins de clés / valeurs. sessionStorage, ne conserve pas les données, qui sont effacées lorsque la session se termine, tandis que localStorage conserve les données entre les sessions

Le stockage local / de session a l'inconvénient d'être limité à une taille petite (et incohérente), la mise en œuvre des navigateurs offrant de 2 Mo à 10 Mo d'espace par site.

Dans le passé, nous avions également Web SQL , un wrapper autour de SQLite, mais maintenant cela est obsolète et non pris en charge sur certains navigateurs modernes, cela n'a jamais été un standard reconnu et il ne devrait donc pas être utilisé, bien que 83% des utilisateurs aient cette technologie sur leur appareils selon Can I Use.

Bien que vous puissiez techniquement créer plusieurs bases de données par site, vous créez généralement une seule base de données et, à l'intérieur de cette base de données, vous pouvez créer plusieurs magasins d'objets .

Une base de données est privée pour un domaine , de sorte qu'aucun autre site ne peut accéder à un autre site Web des magasins IndexedDB.

Chaque magasin contient généralement un ensemble de choses , qui peuvent être

  • cordes
  • Nombres
  • objets
  • tableaux
  • Rendez-vous

Par exemple, vous pouvez avoir un magasin contenant des articles, un autre contenant des commentaires.

Un magasin contient un certain nombre d'éléments qui ont une clé unique, qui représente la façon dont un objet peut être identifié.

Vous pouvez modifier ces magasins à l'aide de transactions, en effectuant des opérations d'ajout, de modification et de suppression et en effectuant une itération sur les éléments qu'ils contiennent.

Depuis l'avènement des promesses dans ES6, et le passage ultérieur des API à l'utilisation des promesses, l'API IndexedDB semble un peu vieille école .

Bien qu'il n'y ait rien de mal à cela, dans tous les exemples que j'expliquerai, j'utiliserai la bibliothèque promise IndexedDB de Jake Archibald, qui est une petite couche au-dessus de l'API IndexedDB pour la rendre plus facile à utiliser.

Cette bibliothèque est également utilisée sur tous les exemples sur le site Web de Google Developers concernant IndexedDB

Créer une base de données IndexedDB

Le moyen le plus simple est d'utiliser unpkg , en ajoutant ceci à l'en-tête de la page:

 import { openDB, deleteDB } from '//unpkg.com/idb?module'  

Avant d'utiliser l'API IndexedDB, assurez-vous toujours de vérifier la prise en charge dans le navigateur, même si elle est largement disponible, vous ne savez jamais quel navigateur l'utilisateur utilise:

(() => { 'use strict' if (!('indexedDB' in window)) { console.warn('IndexedDB not supported') return } //...IndexedDB code })() 

Comment créer une base de données

Utilisation openDB():

(async () => { //... const dbName = 'mydbname' const storeName = 'store1' const version = 1 //versions start at 1 const db = await openDB(dbName, version, { upgrade(db, oldVersion, newVersion, transaction) { const store = db.createObjectStore(storeName) } }) })() 

Les 2 premiers paramètres sont le nom de la base de données et le verson. Le troisième paramètre, facultatif, est un objet qui contient une fonction appelée uniquement si le numéro de version est supérieur à la version actuelle de la base de données installée . Dans le corps de la fonction, vous pouvez mettre à jour la structure (magasins et index) de la base de données.

Ajouter des données dans un magasin

Ajouter des données lors de la création du magasin, l'initialiser

Vous utilisez la putméthode de la boutique d'objets, mais nous avons d'abord besoin d'une référence à celle-ci, que nous pouvons obtenir db.createObjectStore()lorsque nous la créons.

Lors de l'utilisation put, la valeur est le premier argument, la clé est le second. En effet, si vous spécifiez keyPathlors de la création du magasin d'objets, vous n'avez pas besoin de saisir le nom de la clé à chaque requête put (), vous pouvez simplement écrire la valeur.

Celui-ci se remplit store0dès que nous le créons:

(async () => { //... const dbName = 'mydbname' const storeName = 'store0' const version = 1 const db = await openDB(dbName, version,{ upgrade(db, oldVersion, newVersion, transaction) { const store = db.createObjectStore(storeName) store.put('Hello world!', 'Hello') } }) })() 

Ajout de données lorsque le magasin est déjà créé, à l'aide de transactions

Pour ajouter des éléments plus tard, vous devez créer une transaction de lecture / écriture , qui garantit l'intégrité de la base de données (si une opération échoue, toutes les opérations de la transaction sont annulées et l'état revient à un état connu).

Pour cela, utilisez une référence à l' dbPromiseobjet que nous avons obtenu lors de l'appel openDBet exécutez:

(async () => { //... const dbName = 'mydbname' const storeName = 'store0' const version = 1 const db = await openDB(/* ... */) const tx = db.transaction(storeName, 'readwrite') const store = await tx.objectStore(storeName) const val = 'hey!' const key = 'Hello again' const value = await store.put(val, key) await tx.done })() 

Obtenir des données d'un magasin

Obtenir un article dans un magasin: get()

const key = 'Hello again' const item = await db.transaction(storeName).objectStore(storeName).get(key) 

Obtenir tous les articles d'un magasin: getAll()

Récupérez toutes les clés

const items = await db.transaction(storeName).objectStore(storeName).getAllKeys() 

Obtenez toutes les valeurs stockées

const items = await db.transaction(storeName).objectStore(storeName).getAll() 

Suppression de données d'IndexedDB

Suppression de la base de données, d'un magasin d'objets et des données

Supprimer une base de données IndexedDB entière

const dbName = 'mydbname' await deleteDB(dbName) 

Pour supprimer des données dans un magasin d'objets

Nous utilisons une transaction:

(async () => { //... const dbName = 'mydbname' const storeName = 'store1' const version = 1 const db = await openDB(dbName, version, { upgrade(db, oldVersion, newVersion, transaction) { const store = db.createObjectStore(storeName) } }) const tx = await db.transaction(storeName, 'readwrite') const store = await tx.objectStore(storeName) const key = 'Hello again' await store.delete(key) await tx.done })() 

Migrer depuis la version précédente d'une base de données

The third (optional) parameter of the openDB() function is an object that can contain an upgrade function called only if the version number is higher than the current installed database version. In that function body you can upgrade the structure (stores and indexes) of the db:

const name = 'mydbname' const version = 1 openDB(name, version, { upgrade(db, oldVersion, newVersion, transaction) { console.log(oldVersion) } }) 

In this callback, you can check from which version the user is updating, and perform some operations accordingly.

You can perform a migration from a previous database version using this syntax

(async () => { //... const dbName = 'mydbname' const storeName = 'store0' const version = 1 const db = await openDB(dbName, version, { upgrade(db, oldVersion, newVersion, transaction) { switch (oldVersion) { case 0: // no db created before // a store introduced in version 1 db.createObjectStore('store1') case 1: // a new store in version 2 db.createObjectStore('store2', { keyPath: 'name' }) } db.createObjectStore(storeName) } }) })() 

Unique keys

createObjectStore() as you can see in case 1 accepts a second parameter that indicates the index key of the database. This is very useful when you store objects: put() calls don't need a second parameter, but can just take the value (an object) and the key will be mapped to the object property that has that name.

The index gives you a way to retrieve a value later by that specific key, and it must be unique (every item must have a different key)

A key can be set to auto increment, so you don't need to keep track of it on the client code:

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

Use auto increment if your values do not contain a unique key already (for example, if you collect email addresses without an associated name).

Check if a store exists

You can check if an object store already exists by calling the objectStoreNames() method:

const storeName = 'store1' if (!db.objectStoreNames.contains(storeName)) { db.createObjectStore(storeName) } 

Deleting from IndexedDB

Deleting the database, an object store and data

Delete a database

await deleteDB('mydb') 

Delete an object store

An object store can only be deleted in the callback when opening a db, and that callback is only called if you specify a version higher than the one currently installed:

const db = await openDB('dogsdb', 2, { upgrade(db, oldVersion, newVersion, transaction) { switch (oldVersion) { case 0: // no db created before // a store introduced in version 1 db.createObjectStore('store1') case 1: // delete the old store in version 2, create a new one db.deleteObjectStore('store1') db.createObjectStore('store2') } } }) 

To delete data in an object store use a transaction

const key = 232 //a random key const db = await openDB(/*...*/) const tx = await db.transaction('store', 'readwrite') const store = await tx.objectStore('store') await store.delete(key) await tx.complete 

There's more!

Ce ne sont que les bases. Je n'ai pas parlé de curseurs et de trucs plus avancés. Il y a plus à IndexedDB mais j'espère que cela vous donne une longueur d'avance.

Vous souhaitez apprendre JavaScript? Obtenez mon livre JavaScript sur jshandbook.com