Get to know MDN better
Cette page a été traduite à partir de l'anglais par la communauté. Vous pouvez contribuer en rejoignant la communauté francophone sur MDN Web Docs.
IndexedDB est un moyen de stocker des données de manière persistante dans un navigateur. Cela vous laisse créer des applications web avec de riches possibilités de requêtes indépendamment de la disponibilité du réseau puisque vos applications peuvent fonctionner en ligne ou hors-ligne.
Ce tutoriel vous guide à travers l'utilisation de l'API asynchrone de IndexedDB. Si vous n'êtes pas familier avec le principe de IndexedDB, vous devriez d'abord lire les concepts basiques d'IndexedDB.
Pour la documentation de référence sur l'API d'IndexedDB, voyez l'article IndexedDB et ses sous-parties, qui détaille les types d'objets utilisés par IndexedDB, ainsi que les méthodes sur l'API asynchrone (l'API synchrone a été retirée de la spécification).
Le modèle de base qu'IndexedDB utilise est le suivant :
Maintenant que nous avons ces grands concepts en poche, nous pouvons voir des choses plus concrètes.
Étant donné que la spécification évolue encore, les implémentations actuelles de IndexedDB se cachent sous les préfixes du navigateur. Les fournisseurs de navigateurs peuvent avoir des implémentations différentes de l'API IndexedDB standard jusqu'à ce que la spécification se soit solidifiée. Mais une fois qu'un consensus est atteint sur la norme, les fournisseurs l'implémentent sans les balises de préfixe. Actuellement, certaines implémentations ont supprimé le préfixe : Internet Explorer 10, Firefox 16, Chrome 24. Lorsqu'ils utilisent un préfixe, les navigateurs basés sur Gecko utilisent le préfixe moz, tandis que les navigateurs WebKit utilisent le préfixe webkit.
Au cas où vous souhaiteriez tester votre code dans des navigateurs qui utilisent toujours un préfixe, vous pouvez utiliser le code suivant :
Faites attention aux implémentations qui utilisent un préfixe ; elles peuvent être boguées, incomplètes, voire suivre une ancienne version de la spécification. Il n'est donc pas recommandé d'utiliser en production. Il serait préférable de ne pas supporter ces navigateurs :
On commence l'ensemble du processus comme ceci :
Vous avez vu ? Ouvrir une base de données est comme n'importe quelle autre opération — vous avez juste à le "demander".
La requête "open" n'ouvre pas la base de données ni ne démarre une transaction aussitôt. L'appel de la fonction open() retourne un objet IDBOpenDBRequest avec un résultat (success) ou une valeur d'erreur qui permet de la gérer comme un évènement. La plupart des autres fonctions asynchrones dans IndexedDB fonctionnent de la même façon ; Elles retournent un objet IDBRequest avec le résultat ou une erreur. Le résultat de la fonction "open" est une instance de IDBDatabase.
Le second paramètre de la méthode open est la version de la base de données. La version de la base détermine le schéma de celle-ci — Les objets stockés dans la base de données et leur structure. Si la base de données n'existe pas déjà, elle est créée via l'opération open(), puis, un événement onupgradeneeded est déclenché et vous créez le schéma de la base dans le gestionnaire pour cet événement. Si la base de données existe, mais que vous spécifiez un numéro de version plus élevé, un événement onupgradeneeded est déclenché immédiatement, vous permettant de mettre à jour le schéma dans son gestionnaire – plus d'informations dans Updating the version of the database plus bas et la page référence IDBFactory.open.
Attention : Le numéro de version est un nombre "unsigned long long" ce qui signifie qu'il peut s'agir d'un entier très grand. Cela veut également dire que vous ne pouvez pas utiliser de réél, sinon, il sera converti au nombre entier le plus proche (inférieur) et la transaction peut ne pas démarrer ou ne pas déclencher l'événement upgradeneeded. Par exemple, n'utilisez pas 2.4 comme un numéro de version : var request = indexedDB.open("MyTestDatabase", 2.4); // Ne faites pas ça, même si la version sera arrondie à 2
La première chose que vous ferez avec la plupart des requêtes que vous générerez sera d'ajouter des gestionnaires de succès ou d'erreurs :
Laquelle de ces deux fonctions, onsuccess() or onerror(), sera appelée ? Si tout se passe bien, un évènement success (qui est un évènement DOM dont la propriété type est à "success") est déclenché avec request comme cible. Une fois déclenché, la fonction onsuccess() de request est lancée avec l'évènement success comme argument. S'il y avait un quelconque problème, un évènement erreur (qui est un évènement DOM dont la propriété type est définie à "error") est lancée dans request. Cela déclenche la fonction onerror() avec l'évènement d'erreur comme argument.
L'API IndexedDB est conçue pour minimiser le recours à la gestion des erreurs, donc vous ne serez pas amené à voir beaucoup d'évènements erreurs (du moins, pas tant que vous utilisez l'API !). Cependant, dans le cas d'une ouverture de base de données, il y a quelques conditions qui génèrent des évènements d'erreurs. Le problème le plus courant est que l'utilisateur a décidé d'interdire l'accès à la création de base de données. Un des principaux objectifs d'IndexedDB est de permettre un stockage important de données pour l'utilisation hors-ligne. (Pour en savoir plus sur la capacité de stockage de chaque navigateur, voyez Limites de stockage).
Évidemment, les navigateurs ne peuvent permettre qu'une publicité en ligne ou un site malicieux pollue votre ordinateur, donc ils informent l'utilisateur la première fois qu'une application web tente d'ouvrir un espace de stockage IndexedDB. L'utilisateur peut choisir de permettre ou refuser l'accès. En ce qui concerne l'utilisation d'IndexedDB en mode privé, les données restent en mémoire jusqu'à ce que la session privée soit close (Navigation privée pour Firefox et mode Incognito pour Chrome, mais dans Firefox, cela n'est pas encore implémenté depuis novembre 2015, aussi vous ne pouvez pas utiliser IndexedDB dans le mode privé de Firefo du tout).
Maintenant, en admettant qu'un utilisateur ait accepté la création d'une base, et que vous receviez un évènement "success" qui déclenche le callback (rappel) "success" ; que se passe-il après ? La requête a génèré un appel à indexedDB.open(), donc request.result est une instance de IDBDatabase, et vous voulez garder en mémoire cela pour plus tard. Votre code devrait ressembler à ceci :
Comme mentionné ci-dessus, les évènements d'erreur génèrent des info-bulles. Ils sont rattachés à la requête qui a généré l'erreur, puis la bulle de l'évènement est transmis à la transaction, et enfin à l'objet de la base de données. Si vous souhaitez éviter d'ajouter un gestionnaire d'erreurs à chaque requête, vous pouvez en ajouter un unique à l'objet de la base de donnée, de cette manière :
Une des erreurs courantes possibles lorsqu'on ouvre une base de données, c'est VER_ERR. Celle-ci indique que la version de la base de données stockée sur le disque est supérieure à la version que vous êtes en train d'essayer d'ouvrir. C'est un cas qui doit toujours être pris en considération par le gestionnaire d'erreurs.
Lorsque vous créez une nouvelle base de données, ou que vous augmentez le numéro de version d'une base existante (en spécifiant un numéro de version supérieur à celui que vous aviez auparavant, lors de Ouvrir une base de données), l'évènement onupgradeneeded sera déclenché et un objet IDBVersionChangeEvent sera passé à un évènement onversionchange dans request.result (la variable db dans l'exemple). Dans le gestionnaire d'évènement upgradeneeded, vous devez créer les objets de stockage requis pour cette version de base :
Dans ce cas, la base de données disposera aussitôt des objets de stockage de la version précédente de la base, donc vous n'aurez pas à créer de nouveau ces objets de stockage. Vous aurez seulement besoin de créer de nouveaux objets de stockage, ou d'en supprimer de la version précédente si vous n'en avez plus besoin. Si vous avez besoin de changer un objet de stockage existant (par exemple, pour changer la keyPath), alors vous devez supprimer l'ancien objet de stockage et le créer à nouveau avec les nouveaux paramètres. Notez que ceci supprimera les informations dans l'objet de stockage ! Si vous avez besoin de sauvegarder ces informations, vous devez les lire et les sauvegarder quelque part avant de mettre à jour la base de données.
Essayer de créer un objet de stockage avec un nom déjà existant (ou essayer de supprimer un objet de stockage avec un nom qui n'existe pas encore) renverra une erreur.
Si l'évènement onupgradeneeded quitte avec succès, le gestionnaire onsuccess de la requête d'ouverture de la base de données sera déclenché.
Maintenant, structurons la base de données. IndexedDB utilise des objets de stockage plutôt que des tableaux, et une seule base de données peut contenir un nombre quelconque d'objets de stockage. Chaque fois qu'une valeur est stockée dans un objet de stockage, elle est associée à une clé. Il y a différentes manières pour une clé d'être définie, selon que l'objet de stockage utilise un chemin de clé ou un générateur de clé.
Le tableau suivant montre les différentes manières d'attribuer des clés.
| Non | Non | L'objet de stockage peut contenir n'importe quel type de valeur, même des valeurs primitives comme des nombres ou des chaînes de caractères. Vous devez fournir un argument clé séparé chaque fois que vous souhaitez ajouter une nouvelle valeur. |
| Oui | Non | L'objet de stockage peut contenir des objets JavaScript. Les objets doivent avoir une propriété qui a le même nom que le key path. |
| Non | Oui | L'objet de stockage peut contenir n'importe quel type de valeur. La clé est générée pour vous automatiquement, ou vous pouvez fournir un argument clé séparé si vous voulez utiliser une clé spécifique. |
| Oui | Oui | L'objet de stockage peut contenir des objets JavaScript. Normalement, une clé est générée, et sa valeur est stockée dans l'objet dans une propriété avec le même nom que le key path. Cependant, si une telle propriété existe, sa valeur est utilisée en tant que clé, plutôt que la génération d'une nouvelle clé. |
Vous pouvez aussi créer des index sur un objet de stockage, à condition que l'objet de stockage contienne des objets, et non des primitives. Un index vous permet de consulter les valeurs stockées dans un objet de stockage en utilisant la valeur d'une propriété de l'objet stocké, plutôt que la clé de l'objet.
En outre, les index ont la capacité d'appliquer des contraintes simples sur les données stockées. En paramétrant l'option unique lorsque l'on crée un index, ce dernier fait que deux objets ne peuvent être enregistrés en ayant la même valeur pour le chemin de clé de l'index. Par exemple, si vous avez un objet de stockage qui contient un ensemble de personnes, et que vous voulez vous assurer que deux personnes n'aient pas la même adresse de courriel, vous pouvez utiliser un index avec le paramètre unique à true.
Cela peut sembler confus, mais ce simple exemple devrait illustrer ces concepts. D'abord, nous définissons quelques données client à utiliser dans notre exemple :
Bien sûr, vous n'utiliseriez pas le numéro de sécurité sociale comme clé primaire dans une table clients parce que tout le monde n'a pas de numéro de sécurité sociale, et vous pourriez stocker leur date de naissance au lieu de leur âge, mais laissons ces choix non pertinents pour des raisons de commodité et continuons.
Maintenant, voyons la création d'une base de données pour stocker ces données :
Comme indiqué précédemment, onupgradeneeded est le seul endroit où vous pouvez modifier la structure de la base de données. Dans cette méthode, vous pouvez créer et supprimer des objets de stockage, construire et supprimer des index.
Les objets de stockage sont créés avec un simple appel à createObjectStore(). La méthode prend le nom du stockage et un paramètre de type objet. Même si les paramètres sont optionnels, ils vous laissent définir d'importantes propriétés et redéfinir le type d'un objet de stockage que vous voulez créer. Dans notre cas, nous avons demandé un objet de stockage nommé "customers" et défini un keyPath, qui est la propriété rendant unique un objet individuel dans le stockage. Cette propriété dans l'exemple est "ssn" puisqu'un numéro de sécurité sociale est garanti unique. "ssn" doit être présent sur chaque objet stocké dans objectStore.
Nous avons aussi demandé un index nommé « name » qui examine la propriété name dans les objets stockés. Comme avec createObjectStore(), createIndex() prend un paramètre de type objet facultatif (options) qui définit le type d'index à créer. Ajouter des objets qui n'auront pas de propriété name fonctionnera, mais ces objets n'apparaîtront pas dans l'index « name ».
Nous pouvons récupérer les objets client stockés, en utilisant directement leur ssn dans l'objet de stockage, ou en utilisant leur nom via l'index name. Pour en savoir plus sur ce fonctionnement, se référer à la section Utiliser un index.
Paramétrer un marqueur autoIncrement lorsque l'on crée un objet de stockage activera le générateur de clés pour cet objet de stockage. Par défault, ce marqueur n'est pas défini.
Avec la générateur de clés, une clé sera générée automatiquement lorsque vous ajoutez une valeur dans un objet de stockage. Le compteur initial pour la génération de clés est toujours défini à 1 lorsque l'objet de stockage est créé pour la première fois. Fondamentalement, une nouvelle clé auto-générée sera incrémentée de 1 par rapport à la précédente. Le nombre courant d'un générateur de clé ne décroit jamais, à moins qu'un résultat d'opération sur la base soit annulé, par exemple, l'abandon d'une transaction sur la base. En conséquence, supprimer un enregistrement, voire l'ensemble des enregistrements d'un objet de stockage n'affecte jamais le générateur de clés d'un objet de stockage.
Nous pouvons créer un autre objet de stockage avec un générateur de clés comme ci-dessous :
Pour plus de détails sur le générateur de clés, voyez "W3C Key Generators".
Avant de faire quoi que ce soit avec votre nouvelle base de données, vous aurez besoin de démarrer une transaction. Les transactions viennent de l'objet base de données, et vous devez spécifier sur quel objet vous souhaitez faire pointer la transaction. Une fois dans la transaction, vous pouvez accéder à l'objet de stockage qui contient vos données et faire vos requêtes. Puis, vous devez décider si vous allez appliquer des changements à la base de données, ou si vous avez juste besoin de la lire. Les transactions disposent de trois modes disponibles: readonly (lecture seule), readwrite (lecture/écriture), et versionchange (changement de version).
Pour changer le "schéma" ou la structure de la base de données — qui implique de créer ou supprimer des objets de stockage ou des index — la transaction doit être en mode versionchange. Cette transaction est ouverte en appelant la méthode IDBFactory.open avec une version spécifiée. (Dans les navigateurs WebKit, qui n'ont pas implémenté la dernière spécification, la méthode IDBFactory.open prend seulement un paramètre, le nom de la base de données ; Vous devez donc appeler IDBVersionChangeRequest.setVersion pour établir la transaction versionchange.)
Pour lire les enregistrements d'un objet de stockage existant, la transaction peut être en mode readonly ou readwrite. Pour appliquer des changements à un objet de stockage existant, la transaction doit être en mode readwrite. Vous démarrez ces transactions avec IDBDatabase.transaction. La méthode accepte deux paramètres : les storeNames (la portée, définie comme un tableau des objets de stockage auxquels vous souhaitez accéder) et le mode (readonly ou readwrite) pour la transaction. La méthode retourne un objet de transaction contenant la méthode IDBIndex.objectStore, que vous utilisez pour accéder à votre objet de stockage. Par défaut, lorsqu'aucun mode n'est spécifié, les transactions démarrent en mode readonly.
Note : À partir de Firefox 40, les transactions IndexedDB ont des garanties de durabilité relachées afin d'augmenter les performances (voir bug Firefox 1112702.) Auparavant, lors d'une transaction readwrite IDBTransaction.oncomplete était déclenché seulement lorsque les données étaient garanties pour une écriture sur le disque. Dans Firefox 40+ l'évènement complete est déclenché une fois que l'OS a autorisé l'écriture de données, mais potentiellement avant que les données soient réellement écrites sur le disque. L'évènement complete peut ainsi être livré plus vite qu'avant, cependant, il existe un petit risque que l'ensemble de la transaction soit perdu si l'OS s'effondre ou si un problème électrique survient avant que les données soient écrites. Comme de tels évènements catastrophiques sont rares, la plupart des utilisateurs n'ont pas à s'en soucier. Si vous devez vous assurer de la durabilité pour quelconque raison que ce soit (par exemple, vous stockez des données critiques qui ne peuvent être recalculées plus tard) vous pouvez forcer une transaction à écrire sur le disque avant que l'évènement complete ne soit délivré en créant une transaction utilisant un mode expérimental (non-standard) readwriteflush (se référer à IDBDatabase.transaction.
Vous pouvez accélérer l'accès à vos données en utilisant le bon mode et la bonne portée dans la transaction. Voici deux astuces :
Si vous venez juste de créer une base de données, alors vous souhaitez probablement écrire dedans. Voici comment ça se passe :
La fonction transaction() prend deux arguments (bien que l'un d'eux soit facultatif) et retourne un objet transaction. Le premier argument est une liste d'objets de stockage que la transaction va traiter. Vous pouvez passer un tableau vide si vous voulez que la transaction traite l'ensemble des objets de stockage, mais ne le faites pas, parce que la spécification indique qu'un tableau vide devrait générer une InvalidAccessError. Si vous ne spécifiez rien pour le deuxième argument, vous démarrerez une transaction "read-only" (lecture seule) . Si vous souhaitez aussi écrire, vous devrez passer l'option "readwrite" (lecture/écriture).
Maintenant que vous avez une transaction, vous devez comprendre sa durée de vie. Les transactions sont étroitement liées à la boucle de l'évènement. Si vous établissez une transaction et si vous sortez de la boucle d'évènements sans l'utiliser, alors la transaction deviendra inactive. La seule manière de garder la transaction active est d'y insérer une requête. Lorsque la requête est terminée, vous obtenez un évènement DOM, et en supposant que la requête ait réussi, vous avez une autre opportunité d'étendre la transaction durant ce "callback" (rappel). Si vous sortez de la boucle d'évènements sans étendre la transaction, alors elle devient inactive, etc… Tant qu'il reste des demandes en attente, la transaction reste active. La durée de vie des transactions est vraiment très simple, mais cela peut prendre un peu de temps de la maîtriser. Quelques exemples supplémentaires aideront. Si vous commencez à voir des codes d'erreur TRANSACTION_INACTIVE_ERR, alors vous avez raté quelque chose.
Les transactions peuvent recevoir des évènements DOM de trois types : error (erreur), abort (abandonnée) et complete (terminée). Nous avons déjà parlé du fait que les error créent des bulles, ainsi une transaction peut recevoir des évènements d'erreur venant de n'importe quelle requête l'ayant généré. Un point plus subtil ici, c'est que le comportement par défaut d'une erreur est d'abandonner la transaction là où elle a eu lieu. A moins que vous gériez l'erreur en appelant d'abord stopPropagation() sur l'évènement erreur, puis que vous fassiez quelque chose d'autre, la transaction complète sera annulée. Cette conception vous oblige à réfléchir et gérer les erreurs, mais vous pouvez toujours ajouter un gestionnaire d'erreurs "fourre-tout" à la base de données si la gestion d'erreurs fines est trop lourde. Si vous ne gérez pas un évènement d'erreur, ou si vous appelez abort() sur la transaction, alors la transaction est annulée et un évènement abort est lancé sur la transaction. Sinon, une fois que toutes les demandes en instance sont terminées, vous recevez un évènement complete. Si vous faites beaucoup d'opérations sur les bases de données, alors suivre la transaction plutôt que les requêtes individuelles, peut certainement vous aider.
Maintenant que vous avons une transaction, nous avons besoin de récupérer l'objet de stockage de celle-ci. Les transactions vous permettent seulement d'avoir l'objet de stockage que vous avez spécifié lors de la création de la transaction. Puis, vous pouvez ajouter toutes les données dont vous avez besoin.
La méthode result d'une requête venant d'un appel à add() est la clé de la valeur qui vient d'être ajoutée. Dans ce cas, ce devrait être équivalent à la propriété ssn de l'objet qui vient d'être ajouté, puisque l'objet de stockage utilise la propriété ssn pour le key path. Notez que la fonction add() requiert qu'aucun objet déjà présent dans la base ait la même clé. Si vous essayez de modifier une entrée existante, ou si vous ne vous en occupez pas, vous pouvez utiliser la fonction put(), comme montré plus loin dans la section Mettre à jour une entrée dans la base de données.
Supprimer des données est très similaire :
Maintenant que la base de données dispose de quelques informations, vous pouvez les récupérer de plusieurs façons. D'abord, la plus simple get(). Vous devez fournir une clé pour récupérer la valeur, comme ceci :
Ça fait beaucoup de code pour une "simple" récupération. Voici comment le raccourcir un peu, en supposant que vous gériez les erreurs au niveau de la base de données :
Vous voyez comment ça fonctionne ? Comme il n'y a qu'un seul objet de stockage, vous pouvez éviter de passer une liste d'objets dont vous avez besoin dans votre transaction, et juste passer le nom comme une chaîne de caractères. Aussi, nous faisons seulement une lecture de la base, donc nous n'avons pas besoin d'une transaction "readwrite". Appeler une transaction() sans spécifier de mode nous donne une transaction "readonly". Une autre subtilité ici est que nous n'enregistrons pas l'objet de notre requête dans une variable. Comme l'évènement DOM a la requête comme cible, vous pouvez utiliser l'évènement pour récupérer la propriété result.
Vous pouvez accélérer l'accès à vos données en limitant la portée et le mode de la transaction. Voici deux astuces :
Maintenant que nous avons récupéréré quelques données, les mettre à jour et en insérer est assez simple. Mettons à jour l'exemple précédent :
Ici, nous avons créé une variable objectStore et nous avons recherché un enregistrement d'un client, identifié par la valeur ssn (444-44-4444). Nous avons ensuite mis le résultat dans une variable (data), mis à jour la propriété age de cet objet, puis créé une deuxième requête (requestUpdate) pour mettre l'enregistrement du client dans l'objectStore, en écrasant la valeur précédente.
Note : Dans ce cas, nous avons eu à spécifier une transaction readwrite puisque nous voulions écrire dans la base, et pas seulement la lire.
Utiliser get() nécessite de connaître la clé que vous souhaitez récupérer. Si vous voulez parcourir toutes les valeurs de l'objet de stockage, alors vous devez utiliser un curseur. Voici comment ça marche :
La fonction openCursor() prend en compte plusieurs arguments. En premier, vous pouvez spécifier une plage de résultats à récupérer en utilisant un objet "key range" que nous allons voir dans une minute. En deuxième, vous pouvez spécifier la direction vers laquelle vous souhaitez itérer. Dans l'exemple ci-dessus, nous avons itéré tous les objets dans l'ordre ascendant. Le "callback" (rappel) de réussite pour les curseurs est un peu spécial. L'objet cursor lui-même est le result (résutat) de la requête (au-dessus, nous utilisons le raccourci event.target.result). Puis la clé et valeur courante peuvent être trouvées dans les propriétés key(clé) et value (valeur) de l'objet cursor. Si vous souhaitez continuer, vous devez appeler continue() sur le curseur. Lorsque vous avez atteint la fin des données (ou s'il n'y a plus d'entrées qui correspondent à votre requête openCursor() ) , vous aurez toujours votre callback success, mais la propriété result sera undefined.
Une utilisation classique avec les curseurs est de récupérer tous les objets dans un objet de stockage et de les mettre dans un tableau, comme ceci :
Note : Mozilla a aussi implémenté getAll() pour gérer ce cas (et getAllKeys(), qui est actuellement caché derrière la préférence dom.indexedDB.experimental dans about:config) . ceux-ci ne font pas partie d' IndexedDB standard, et peuvent disparaître dans le futur. Nous les avons inclus partceque nous pensons qu'ils sont utiles. Le code suivant fait exactement la même chose que ci-dessus :
Il y a un coût de performance associé avec la recherche de la propriété value du curseur, parce que l'objet est créé paresseusement. Quand vous utilisez getAll() par exemple, Gecko doit créer tous les objets à la fois. Si vous êtes seulement intéressé par la lecture de chaque clé, pour l'instance, il est beaucoup plus efficace d'utiliser un curseur que getAll(). Si vous essayez d'obtenir un tableau de tous les objets d'un objet de stockage, utilisez getAll().
Le stockage des données des clients utilisant le SSN comme clé est logique puisque le SSN identifie un individu unique. (Que ce soit une bonne idée pour la vie privée est une question différente, et en dehors du champ de cet article). Si vous devez rechercher un client par son nom, vous devrez toutefois faire itérer sur toutes les clés SSN dans la base de données jusqu'à ce que vous trouviez la bonne. La recherche de cette manière serait très lente, alors, vous pouvez utiliser un index.
Le "name" du curseur n'est pas unique, donc il pourrait y avoir plus d'une entrée avec le name attribué à "Donna". Dans ce cas, vous obtenez toujours celui qui a la valeur clé la plus basse .
Si vous avez besoin d'accèder à toutes les entrées avec un name donné, vous pouvez utiliser un curseur. Vous pouvez ouvrir deux types différents de curseurs sur les index. Un curseur normal situe la propriété index de l'objet dans l'objet de stockage. Un curseur de clés situe la propriété index des clés utilisées pour stocker l'objet dans l'objet de stockage. Les différences sont illustrées ici :
Si vous souhaitez limiter l'intervalle de valeurs que vous voyez dans un curseur, vous pouvez utiliser un objet IDBKeyRange et le donner comme premier argument à openCursor() ou openKeyCursor() . Vous pouvez créer un intervalle de clés qui n'autorise qu'une seule clé, ou qui a des limites inférieure et supérieure, ou qui a des bornes inférieure et supérieure. La limite peut être "closed" (fermée) (c'est-à-dire que l'intervalle de clés comprend les valeurs données) ou "open" (ouverte) (c'est-à-dire que la plage de clés n'inclut pas les valeurs données. Voici comment cela fonctionne :
Parfois, vous voudrez peut-être itérer dans l'ordre décroissant plutôt que dans l'ordre croissant (la direction par défaut pour tous les curseurs). Le changement de direction est réalisé en passant prev à la fonction openCursor() antérieure comme second argument :
Si vous souhaitez simplement spécifier un changement de direction, mais ne pas limiter les résultats, vous pouvez simplement passer "null" comme premier argument :
Étant donné que l'index "name" n'est pas unique, il peut y avoir plusieurs entrées où le name est le même. Notez qu'une telle situation ne peut pas se produire avec les objets stockés car la clé doit toujours être unique. Si vous souhaitez filtrer les doublons pendant l'itération du curseur sur les index, vous pouvez passer nextunique (ou prevunique si vous revenez en arrière) comme paramètre de direction. Lorsque nextunique ou prevunique sont utilisés, l'entrée avec la clé la plus basse est toujours celle retournée.
Voyez "IDBCursor Constants" pour les arguments de direction valides.
Lorsque votre application Web change de telle sorte qu'une modification de version est nécessaire pour votre base de données, vous devez considérer ce qui se passe si l'utilisateur a l'ancienne version de votre application ouverte dans un onglet, puis charge la nouvelle version de votre application dans une autre . Lorsque vous appelez open() avec une version plus grande que la version actuelle de la base de données, toutes les autres bases de données ouvertes doivent reconnaître explicitement la demande avant de commencer à modifier la base de données (un événement onblocked (bloqué) est déclenché jusqu'à ce qu'elles soient fermées ou rechargées). Voici comment cela fonctionne :
Vous devriez également écouter les erreurs VersionError pour gérer le cas où les applications déjà ouvertes déclencheraient un code conduisant à une nouvelle tentative d'ouverture de la base de données, mais en utilisant une version désuète.
IndexedDB utilise le principe " same-origin " (même origine), ce qui signifie qu'il relie le stockage à l'origine du site qui le crée (généralement, c'est le domaine ou le sous-domaine du site), de sorte qu'il ne peut être consulté par aucune autre origine.
Le contenu de la fenêtre de tiers (par exemple le contenu de <iframe>) peut accèder à IndexedDB pour l'origine dans laquelle il est intégré, à moins que le navigateur ne soit configuré pour ne jamais accepter de cookies tiers (voir le bug Firefox 1147821).
Lorsque le navigateur s'arrête (parce que l'utilisateur a choisi l'option Quit ou Exit), le disque contenant la base de données est supprimé de manière inattendue ou les permissions sont perdues dans le magasin de base de données, les choses suivantes se produisent :
Le comportement décrit ci-dessus est nouveau et n'est disponible que pour les versions de navigateur suivantes : Firefox 50, Google Chrome 31 (approximativement).
Avant ces versions de navigateurs, les transactions étaient interrompues silencieusement et aucun événement close n'était déclenché, donc il n'y avait aucun moyen de détecter une fermeture de base de données inattendue.
Étant donné que l'utilisateur peut quitter le navigateur à tout moment, cela signifie que vous ne pouvez pas compter sur une transaction particulière à compléter, et sur les navigateurs plus anciens, vous n'êtes même pas informé quand elles ne sont pas terminées. Il y a plusieurs conséquences à ce comportement.
Tout d'abord, vous devez vous occuper de toujours laisser votre base de données dans un état cohérent à la fin de chaque transaction. Par exemple, supposons que vous utilisiez IndexedDB pour stocker une liste d'éléments que l'utilisateur est autorisé à éditer. Vous enregistrez la liste après l'édition en effaçant l'objet de stockage puis en écrivant la nouvelle liste. Si vous effacez l'objet de stockage dans une transaction et que vous écrivez la nouvelle liste dans une autre transaction, il existe un danger : si le navigateur se ferme après l'effacement mais avant l'écriture, votre base de données est vide. Pour éviter cela, vous devez combiner l'effacement et l'écriture en une seule transaction.
Ensuite, vous ne devez jamais lier les transactions de base de données pour les événements unload (déchargement). Si l'événement unload est déclenché par la fermeture du navigateur, toutes les transactions créées dans le gestionnaire d'événements unload ne seront jamais terminées. Une approche intuitive, pour le maintien de certaines informations dans les sessions du navigateur, est de le lire à partir de la base de données, lorsque le navigateur (ou une page particulière) est ouvert, le mettre à jour à mesure que l'utilisateur interagit avec le navigateur, puis l'enregistrer dans la base de données lorsque le navigateur ( ou page) se ferme. Cependant, cela ne fonctionne pas. Les transactions de la base de données sont créées dans le gestionnaire d'événements unload, mais comme elles sont asynchrones, elles sont interrompues avant qu'elles puissent s'exécuter.
En fait, il n'y a aucun moyen de garantir que les transactions IndexedDB seront terminées, même avec un arrêt normal du navigateur. Voir le bug Firefox 870645. Comme solution de rechange pour cette notification d'arrêt normal, vous pouvez suivre vos transactions et ajouter un événement beforeunload pour avertir l'utilisateur si des transactions ne sont pas encore terminées au moment du déchargement.
Au-moins, avec l'ajout des notifications d'annulation et IDBDatabase.onclose, vous pouvez savoir quand cela s'est produit.
Mozilla a implémenté la capacité d'effectuer un tri des données IndexedDB localisées sur Firefox 43+. Par défaut, IndexedDB n'a pas pris en charge l'internationalisation des chaînes de tri, et était trié comme s'il s'agissait d'un texte anglais. Par exemple, "b", "á", "z", "a" devaient être triés comme suit :
ce qui n'est évidemment pas la façon dont les utilisateurs souhaitent que leurs données soient triées - Aaron et Áaron, par exemple, doivent aller l'un à côté de l'autre dans une liste de contacts. L'obtention d'un tri international approprié exige donc que l'ensemble des données soit appelé dans la mémoire et que le tri soit exécuté par le JavaScript côté client, ce qui n'est pas très efficace.
Cette nouvelle fonctionnalité permet aux développeurs de spécifier une "locale" (langue) lors de la création d'un index en utilisant IDBObjectStore.createIndex() (vérifiez ses paramètres). Dans ce cas, lorsqu'un curseur est utilisé pour itérer sur l'ensemble de données , et si vous souhaitez spécifier un tri local, vous pouvez utiliser un IDBLocaleAwareKeyRange.
IDBIndex a également eu de nouvelles propriétés qui lui ont été ajoutées pour spécifier la langue : locale (retourne la langue si elle est spécifiée, ou null sinon) et isAutoLocale (retourne true (vrai) si l'index a été créé avec une "locale auto", ce qui signifie que la langue par défaut de la plate-forme est utilisée, sinon false).
Note : Cette fonctionnalité est couramment cachée derrière une marque (flag) — pour l'activer et l'expérimenter, aller à about:config et activez dom.indexedDB.experimental.
Référence :
Tutoriels :
Bibliothèques :
Cette page a été modifiée le 17 déc. 2024 par les contributeur·ice·s du MDN.
Votre modèle pour un internet meilleur.
Visitez la société mère à but non lucratif de Mozilla Corporation, la Fondation Mozilla.
Certaines parties de ce contenu sont protégées par le droit d'auteur ©1998—2026 des contributeurs individuels de mozilla.org. Contenu disponible sous une licence Creative Commons.