IndexedDB est l’une des solution de stockage introduites dans les navigateurs au fil des années.
Il s’agit d’une base de données clé/valeur (une base de données noSQL) considérée comme la solution définitive pour le stockage des données dans les navigateurs.
C’est une API asynchrone, ce qui signifie que l’exécution du code ne bloquera pas le reste de l’execution de votre programme, offrant une expérience non négligeable aux utilisateurs. Il peut stocker une quantité indéfinie de données, bien qu’une fois au-dessus d’un certain seuil, l’utilisateur est invité à autoriser le site à pouvoir stocker plus de données.
Ils existent d’autres méthodes, les cookies et le Web Storage ( localStorage et sessionStorage ), Le stockage local/session a l’inconvénient d’être limité à une petite taille, les navigateurs offrant du 2MB à 10MB d’espace par site.
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 stockage d’objets.
Une base de données est privée à un domaine, de sorte que tout autre site ne peut pas accéder à une autre base IndexedDB.
Chaque magasin d’objets contient généralement un ensemble de choses, qui peuvent être utilisé :
- objects
- arrays
- strings
- numbers
- dates
Par exemple, vous pouvez avoir un magasin qui contient des messages, un autre qui contient des commentaires.
Un magasin contient un certain nombre de d’objets qui possèdent une clé unique, qui représente la façon dont un objet peut être identifié.
Vous pouvez modifier ces objets à l’aide de transactions, en effectuant des opérations d’ajout, de modification, de suppression et d’itération sur les éléments qu’ils contiennent.
Depuis l’avènement de Promises dans ES6, et le passage subséquent 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 sur l’API IndexedDB pour la rendre plus facile à utiliser.
Cette bibliothèque est également utilisée sur tous les exemples sur le site Google Developers concernant IndexedDB
Créer une base de données IndexedDB
La façon la plus simple est d’utiliser unpkg, en l’ajoutant à l’en-tête de la page :
<script type="module">
import { openDB, deleteDB } from 'https://unpkg.com/idb?module'
</script>
Avant d’utiliser l’API IndexedDB, assurez-vous de toujours vérifier si le navigateur est compatible, même s’il est largement disponible, vous ne savez jamais quel navigateur l’utilisateur utilise :
(async () => {
'use strict'
if (!('indexedDB' in window)) {
console.warn('IndexedDB not supported')
return
}
})()
Comment créer une base de données IndexedDB
Utiliser openDB():
(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)
}
})
})()
Les 2 premiers paramètres sont le nom de la base de données et le version. Le troisième paramètre, qui est 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 fonction, vous pouvez mettre à niveau la structure (points de vente et index) de la base de données.
Ajout de données dans un store
Ajout de données lors de la création du store, initialisation du store
Pour ajouter des données, il faut utiliser la méthode put
du store d’objets, mais nous devons d’abord initialiser celui-ci avec db.createObjectStore()
lorsque nous le créons.
Lors de l’utilisation de put, la valeur est le premier argument, la clé est le second. C’est parce que si vous spécifiez keyPath
lors de la création du store d’objets, vous n’avez pas besoin d’entrer le nom de clé sur chaque requête put()
, vous pouvez simplement écrire la valeur.
Ceci remplit store0 dès que nous l’avons créé :
(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 point de vente est déjà créé, à l’aide de transactions
Pour ajouter des éléments plus tard, vous devez créer une transaction en lecture/écriture qui assure 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 à son état initiale).
Pour cela, utilisez une référence à l’objet dbPromise que nous avons obtenu en appelant openDB, et lancez :
(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 point de vente
Obtenir un article d’un store d’objets: get()
const key = 'Hello again'
const item = await db.transaction(storeName).objectStore(storeName).get(key)
Obtenir tous les articles d’un store d’objets: getAll()
Obtenir toutes les clés stockées
const items = await db.transaction(storeName).objectStore(storeName).getAllKeys()
Obtenir 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 store d’objets et de données
Supprimer entièrement une base de données IndexedDB
const dbName="mydbname"
await deleteDB(dbName)
Pour supprimer des données dans un store d’objets
Nous utilisons une transaction :
(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)
}
})
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 à partir d’une version précédente d’une base de données
Le troisième paramètre (facultatif) de la fonction openDB() est un objet qui peut contenir une fonction de mise à jour appelée uniquement si le numéro de version est supérieur à la version de base de données installée. Dans ce corps de fonction, vous pouvez mettre à jour la structure (points de vente et index) de la base de données :
const name="mydbname"
const version = 1
openDB(name, version, {
upgrade(db, oldVersion, newVersion, transaction) {
console.log(oldVersion)
}
})
Dans ce rappel, vous pouvez vérifier à partir de quelle version l’utilisateur effectue la mise à jour et effectuer certaines opérations en conséquence.
Vous pouvez effectuer une migration à partir d’une version de base de données précédente en utilisant cette syntaxe
(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:
// un store introduit dans la version 1
db.createObjectStore('store1')
case 1:
// supprimer l'ancien point de vente dans la version 2, en créer un nouveau
db.createObjectStore('store2', { keyPath: 'name' })
}
db.createObjectStore(storeName)
}
})
})()
Clé unique
createObjectStore()
comme vous pouvez le voir dans le cas où 1 accepte un deuxième paramètre qui indique la clé d’index de la base de données. Ceci est très utile lorsque vous stockez des objets : les appels put()
n’ont pas besoin d’un second paramètre, mais peuvent simplement prendre la valeur (un objet) et la clé sera mappée à la propriété de l’objet qui a ce nom.
L’index vous donne un moyen de récupérer une valeur plus tard par cette clé spécifique, et il doit être unique (chaque élément doit avoir une clé différente).
Une clé peut être réglée sur incrémentation automatique, vous n’avez donc pas besoin de la conserver sur le code client :
db.createObjectStore('notes', { autoIncrement: true })
Utilisez l’incrémentation automatique si vos valeurs ne contiennent pas déjà une clé unique.
Vérifier l’existence d’un store d’objets
Vous pouvez vérifier si un store d’objets existe déjà en appelant la méthode objectStoreNames()
:
const storeName="store1"
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName)
}
Supprimer d’IndexedDB
Suppression de la base de données, d’un store d’objets et de données
Supprimer une base de données
await deleteDB('mydb')
Supprimer un store d’objets
Une liste d’objets ne peut être supprimée dans le callback qu’à l’ouverture d’une db, et ce callback n’est appelé que si vous spécifiez une version supérieure à celle qui est actuellement installée :
const db = await openDB('dogsdb', 2, {
upgrade(db, oldVersion, newVersion, transaction) {
switch (oldVersion) {
case 0:
// un store introduit dans la version 1
db.createObjectStore('store1')
case 1:
// supprimer l'ancien point de vente dans la version 2, en créer un nouveau
db.deleteObjectStore('store1')
db.createObjectStore('store2')
}
}
})
Pour supprimer des données dans un store d’objets, utilisez une transaction
const key = 232
const db = await openDB(/*...*/)
const tx = await db.transaction('store', 'readwrite')
const store = await tx.objectStore('store')
await store.delete(key)
await tx.complete