Een snelle maar complete gids voor geïndexeerde DB en het opslaan van gegevens in browsers

Wilt u JavaScript leren? Ontvang mijn gratis ebook op jshandbook.com

IndexedDB is een van de opslagmogelijkheden die in de loop der jaren in browsers zijn geïntroduceerd. Het is een sleutel / waarde-opslag (een noSQL-database) die wordt beschouwd als de definitieve oplossing voor het opslaan van gegevens in browsers.

IndexedDB is een asynchrone API, wat betekent dat het uitvoeren van dure bewerkingen de gebruikersinterface niet blokkeert en gebruikers een slordige ervaring biedt.

Het kan een onbeperkte hoeveelheid gegevens opslaan, hoewel de gebruiker eenmaal boven een bepaalde drempel wordt gevraagd om de site hogere limieten te geven.

Het wordt ondersteund door alle moderne browsers.

Het ondersteunt transacties en versiebeheer en levert goede prestaties.

In de browser kunnen we ook gebruiken:

  • Cookies, die een zeer klein aantal strings kunnen bevatten
  • DOM Storage (of Web Storage), een term die meestal localStorage en sessionStorage identificeert, twee sleutel / waarde-winkels. sessionStorage, bewaart geen gegevens, die worden gewist wanneer de sessie eindigt, terwijl localStorage de gegevens gedurende sessies bewaart

Lokale / sessie-opslag heeft het nadeel dat deze is beperkt tot een kleine (en inconsistente) grootte, met de implementatie van browsers die 2 MB tot 10 MB ruimte per site bieden.

In het verleden hadden we ook Web SQL, een wrapper rond SQLite. Maar dit is nu verouderd en wordt niet ondersteund in sommige moderne browsers. Het is nooit een erkende standaard geweest, en daarom moet het niet worden gebruikt - hoewel 83% van de gebruikers deze technologie op hun apparaten heeft volgens Kan ik gebruiken.

Hoewel u technisch gezien meerdere databases per site kunt maken, maakt u over het algemeen één database. Binnen die database kunt u meerdere objectarchieven maken.

Een database is privé voor een domein, dus elke andere site heeft geen toegang tot de IndexedDB-winkels van een andere website.

Elke winkel bevat meestal een aantal dingen, die kunnen zijn:

  • strings
  • getallen
  • voorwerpen
  • arrays
  • data

U hebt bijvoorbeeld een winkel met berichten en een andere met opmerkingen.

Een winkel bevat een aantal items met een unieke sleutel, die de manier aangeeft waarop een object kan worden geïdentificeerd.

U kunt die winkels wijzigen met behulp van transacties, door handelingen voor toevoegen, bewerken en verwijderen uit te voeren en door de items die ze bevatten te herhalen.

Sinds de komst van Promises in ES2015 en de daaropvolgende verplaatsing van API's naar het gebruik van beloften, lijkt de IndexedDB API een beetje ouderwets.

Hoewel er niets mis mee is, gebruik ik in alle voorbeelden die ik uitleg de IndexedDB Promised Library van Jake Archibald, een kleine laag bovenop de IndexedDB API om het gebruik eenvoudiger te maken.

Deze bibliotheek wordt ook gebruikt in alle voorbeelden op de Google Developers-website met betrekking tot IndexedDB.

Maak een geïndexeerde DB-database

Neem de idb-lib op met:

garen toevoegen idb

En neem het vervolgens op in uw pagina, met behulp van Webpack of Browserify of een ander buildsysteem, of gewoon:

En we zijn klaar om te gaan.

Voordat u de IndexedDB API gebruikt, moet u altijd controleren op ondersteuning in de browser, ook al is deze overal beschikbaar. Je weet nooit welke browser de gebruiker gebruikt:

(() => {
  'gebruik streng'
  if (! ('indexedDB' in venster)) {
    console.warn ('Geïndexeerde DB niet ondersteund')
    terugkeer
  }
//...IndexedDB code
}) ()

Een database maken

Idb.open () gebruiken:

const name = 'mydbname'
const-versie = 1 // versies beginnen bij 1
idb.open (naam, versie, upgradeDb => {})

De eerste twee parameters spreken voor zich. De derde parameter, die optioneel is, wordt alleen teruggebeld als het versienummer hoger is dan de huidige geïnstalleerde databaseversie. In de callback-functie kunt u de structuur (winkels en indexen) van de DB upgraden.

We gebruiken de naam upgradeDB voor de callback om aan te geven dat dit het moment is om de database indien nodig bij te werken.

Maak een Object Store

Een objectarchief maken of een nieuwe toevoegen

Een objectarchief wordt in deze callback gemaakt of bijgewerkt met de syntaxis db.createObjectStore ('storeName', opties):

const dbPromise = idb.open ('mydb', 1, (upgradeDB) => {
  upgradeDB.createObjectStore ( 'store1')
})
.then (db => console.log ('success'))

Als u een vorige versie hebt geïnstalleerd, kunt u met de callback de migratie uitvoeren:

const dbPromise = idb.open ('keyval-store', 3, (upgradeDB) => {
  schakelaar (upgradeDB.oldVersion) {
    case 0: // no DB eerder gemaakt
      // een winkel geïntroduceerd in versie 1
      upgradeDB.createObjectStore ( 'store1')
    zaak 1:
      // een nieuwe winkel in versie 2
      upgradeDB.createObjectStore ('store2', {keyPath: 'name'})
  }
})
.then (db => console.log ('success'))

createObjectStore () zoals u kunt zien in geval 1 accepteert een tweede parameter die de indexsleutel van de database aangeeft. Dit is erg handig wanneer u objecten opslaat: put () -aanroepen hebben geen tweede parameter nodig, maar kunnen alleen de waarde (een object) aannemen en de sleutel wordt toegewezen aan de objecteigenschap die die naam heeft.

De index biedt u een manier om later een waarde op te halen met die specifieke sleutel en deze moet uniek zijn (elk item moet een andere sleutel hebben).

Een sleutel kan worden ingesteld op automatisch verhogen, zodat u deze niet hoeft bij te houden op de klantcode. Als u geen sleutel opgeeft, maakt IndexedDB deze voor ons transparant:

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

maar u kunt ook een specifiek veld met objectwaarde opgeven voor automatisch verhogen:

upgradeDb.createObjectStore ('notes', {
  keyPath: 'id',
  autoIncrement: waar
})

Gebruik als algemene regel automatisch verhogen als uw waarden nog geen unieke sleutel bevatten (bijvoorbeeld een e-mailadres voor gebruikers).

Indexes

Een index is een manier om gegevens uit het objectarchief op te halen. Het wordt samen met het maken van de database in de callback idb.open () op deze manier gedefinieerd:

const dbPromise = idb.open ('dogsdb', 1, (upgradeDB) => {
  const dogs = upgradeDB.createObjectStore ('dogs')
  dogs.createIndex ('name', 'name', {unique: false})
})

De unieke optie bepaalt of de indexwaarde uniek moet zijn en er geen dubbele waarden mogen worden toegevoegd.

U kunt een objectarchief openen dat al is gemaakt met de methode upgradeDb.transaction.objectStore ():

const dbPromise = idb.open ('dogsdb', 1, (upgradeDB) => {
  const dogs = upgradeDB.transaction.objectStore ('dogs')
  dogs.createIndex ('name', 'name', {unique: false})
})

Controleer of er een winkel bestaat

U kunt controleren of er al een objectarchief bestaat door de methode objectStoreNames () aan te roepen:

if (! upgradeDb.objectStoreNames.contains ('store3')) {
  upgradeDb.createObjectStore ( 'store3')
}

Verwijderen uit geïndexeerde DB

Verwijder een database

idb.delete ('mydb'). then (() => console.log ('done'))

Verwijder een objectarchief

Een objectarchief kan alleen worden verwijderd in de callback bij het openen van een DB en die callback wordt alleen aangeroepen als u een versie opgeeft die hoger is dan de versie die momenteel is geïnstalleerd:

const dbPromise = idb.open ('dogsdb', 2, (upgradeDB) => {
  upgradeDB.deleteObjectStore ( 'old_store')
})

Gebruik deze transactie om gegevens in een objectarchief te verwijderen:

const-toets = 232
dbPromise.then ((db) => {
  const tx = db.transaction ('store', 'readwrite')
  const store = tx.objectStore ('store')
  store.delete (sleutel)
  retourneer tx.complete
})
.then (() => {
  console.log ('Item verwijderd')
})

Voeg een item toe aan de database

U kunt de put-methode van het objectarchief gebruiken, maar eerst hebben we er een verwijzing naar nodig, die we kunnen krijgen van upgradeDB.createObjectStore () wanneer we het maken.

Bij het gebruik van put is de waarde het eerste argument en de sleutel het tweede. Dit komt omdat als u keyPath opgeeft bij het maken van het objectarchief, u niet de sleutelnaam hoeft in te voeren bij elk put () -verzoek. Je kunt gewoon de waarde schrijven.

Dit vult store0 zodra we het maken:

idb.open ('mydb', 1, (upgradeDB) => {
  keyValStore = upgradeDB.createObjectStore ('store0')
  keyValStore.put ('Hallo wereld!', 'Hallo')
})

Als u later items wilt toevoegen, moet u een transactie maken. Dat zorgt voor de integriteit van de database (als een bewerking mislukt, worden alle bewerkingen in de transactie teruggedraaid en gaat de status terug naar een bekende status).

Gebruik daarvoor een verwijzing naar het dbPromise-object dat we hebben gekregen bij het aanroepen van idb.open () en voer uit:

dbPromise.then ((db) => {
  const val = 'hey!'
  const key = 'Hallo weer'
  const tx = db.transaction ('store1', 'readwrite')
  tx.objectStore ('store1'). put (val, key)
  retourneer tx.complete
})
.then (() => {
  console.log ('Transactie voltooid')
})
.catch (() => {
  console.log ('Transactie mislukt')
})

De IndexedDB API biedt ook de add () -methode, maar omdat put () ons in staat stelt om zowel toe te voegen als bij te werken, is het eenvoudiger om het gewoon te gebruiken.

Items uit een winkel halen

Een specifiek item uit een winkel krijgen met get ()

dbPromise.then (db => db.transaction ('objs')
                       .objectStore (Objs)
                       .get (123456))
.then (obj => console.log (obj))

Alle items ophalen met getAll ()

dbPromise.then (db => db.transaction ('store1')
                       .objectStore (store1)
                       .alles krijgen())
.then (objects => console.log (objecten))

Alle items herhalen met een cursor via openCursor ()

dbPromise.then ((db) => {
  const tx = db.transaction ('store', 'readonly')
  const store = tx.objectStore ('store')
  retour store.openCursor ()
})
.then (functie logitems (cursor) {
  if (! cursor) {return}
  console.log ('cursor is at:', cursor.key)
  voor (const veld in cursor.value) {
    console.log (cursor.value [field])
  }
  return cursor.continue (). then (logItems)
})
.then (() => {
  console.log ( 'gedaan!')
})

Een deel van de items herhalen met behulp van grenzen en cursors

const searchItems = (onderste, bovenste) => {
  if (onderste === '' && upper === '') {retour}
  laat bereik
  if (lager! == '' && upper! == '') {
    bereik = IDBKeyRange.bound (onderste, bovenste)
  } anders if (lager === '') {
    bereik = IDBKeyRange.upperBound (bovenste)
  } anders {
    bereik = IDBKeyRange.lowerBound (lager)
  }
  dbPromise.then ((db) => {
    const tx = db.transaction (['dogs'], 'readonly')
    const store = tx.objectStore ('dogs')
    const index = store.index ('age')
    return index.openCursor (bereik)
  })
  .then (functie showRange (cursor) {
    if (! cursor) {return}
    console.log ('cursor is at:', cursor.key)
    voor (const veld in cursor.value) {
      console.log (cursor.value [field])
    }
    return cursor.continue (). then (showRange)
  })
  .then (() => {
    console.log ( 'gedaan!')
  })
}
searchDogsBetweenAges (3, 10)
Wilt u JavaScript leren? Ontvang mijn gratis ebook op jshandbook.com