JavaScript und IndexedDB
In diesem Artikel erklären wir JavaScript und IndexedDB.
Dieses Tutorial bietet eine schrittweise Erklärung von JavaScript und IndexedDB
mit praktischem Beispielcode bei jedem Schritt, um Ihr Verständnis zu vertiefen.
YouTube Video
javascript-indexed-db.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 .container-flex {
32 display: flex;
33 flex-wrap: wrap;
34 gap: 2em;
35 max-width: 1000px;
36 margin: 0 auto;
37 padding: 1em;
38 background-color: #ffffff;
39 border: 1px solid #ccc;
40 border-radius: 10px;
41 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
42 }
43
44 .left-column, .right-column {
45 flex: 1 1 200px;
46 min-width: 200px;
47 }
48
49 h1, h2 {
50 font-size: 1.2rem;
51 color: #007bff;
52 margin-top: 0.5em;
53 margin-bottom: 0.5em;
54 border-left: 5px solid #007bff;
55 padding-left: 0.6em;
56 background-color: #e9f2ff;
57 }
58
59 button {
60 display: block;
61 margin: 1em auto;
62 padding: 0.75em 1.5em;
63 font-size: 1rem;
64 background-color: #007bff;
65 color: white;
66 border: none;
67 border-radius: 6px;
68 cursor: pointer;
69 transition: background-color 0.3s ease;
70 }
71
72 button:hover {
73 background-color: #0056b3;
74 }
75
76 #output {
77 margin-top: 1em;
78 background-color: #1e1e1e;
79 color: #0f0;
80 padding: 1em;
81 border-radius: 8px;
82 min-height: 200px;
83 font-family: Consolas, monospace;
84 font-size: 0.95rem;
85 overflow-y: auto;
86 white-space: pre-wrap;
87 }
88
89 .highlight {
90 outline: 3px solid #ffc107; /* yellow border */
91 background-color: #fff8e1; /* soft yellow background */
92 transition: background-color 0.3s ease, outline 0.3s ease;
93 }
94
95 .active {
96 background-color: #28a745; /* green background */
97 color: #fff;
98 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
99 transition: background-color 0.3s ease, box-shadow 0.3s ease;
100 }
101 </style>
102</head>
103<body>
104 <div class="container">
105 <h1>JavaScript Console</h1>
106 <button id="executeBtn">Execute</button>
107 <div id="output"></div>
108 </div>
109
110 <script>
111 // Override console.log to display messages in the #output element
112 (function () {
113 // Override console.log
114 const originalLog = console.log;
115 console.log = function (...args) {
116 originalLog.apply(console, args);
117 const message = document.createElement('div');
118 message.textContent = args
119 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
120 .join(" ");
121 output.appendChild(message);
122 };
123
124 // Override console.error
125 const originalError = console.error;
126 console.error = function (...args) {
127 originalError.apply(console, args);
128 const message = document.createElement('div');
129 message.textContent = args
130 .map(arg => (typeof arg === "object" && arg !== null ? JSON.stringify(arg) : String(arg)))
131 .join(" ");
132 message.style.color = 'red'; // Color error messages red
133 output.appendChild(message);
134 };
135 })();
136
137 document.getElementById('executeBtn').addEventListener('click', () => {
138 // Prevent multiple loads
139 if (document.getElementById('externalScript')) return;
140
141 const script = document.createElement('script');
142 script.src = 'javascript-indexed-db.js';
143 script.id = 'externalScript';
144 //script.onload = () => console.log('javascript-indexed-db.js loaded and executed.');
145 //script.onerror = () => console.log('Failed to load javascript-indexed-db.js.');
146 document.body.appendChild(script);
147 });
148 </script>
149</body>
150</html>
JavaScript und IndexedDB
IndexedDB
ist eine asynchrone Key-Value-Datenbank, die in Browser integriert ist. Sie bietet Funktionen ähnlich wie relationale Datenbanken und ermöglicht es, große Mengen strukturierter Daten auf der Client-Seite zu speichern und zu durchsuchen. Sie ist besonders nützlich für offlinefähige Anwendungen und PWAs (Progressive Web Apps).
Funktionen von IndexedDB
- Funktioniert asynchron und ereignisgesteuert.
- JavaScript-Objekte können in Object Stores gespeichert werden.
- Suche mittels Abfragen oder Indizes ist möglich.
- Sie hat eine große Speicherkapazität (Hunderte von MB oder mehr), sodass Sie deutlich mehr Daten als mit Cookies oder localStorage speichern können.
Öffnen und Erstellen einer Datenbank
Um IndexedDB
zu verwenden, müssen Sie zunächst eine Datenbank öffnen. Existiert sie nicht, wird sie automatisch erstellt.
1const request = indexedDB.open('MyDatabase', 1); // Specify DB name and version
- Die
open
-Methode öffnet die Datenbank asynchron und löst die folgenden drei Ereignisse aus.
onsuccess
1// Fired when database is successfully opened
2request.onsuccess = (event) => {
3 const db = event.target.result; // Database instance
4 console.log('Database opened successfully:', db.name);
5};
- Das
onsuccess
-Ereignis wird ausgelöst, wenn die Datenbank erfolgreich geöffnet wurde. Nachfolgende Operationen sollten überrequest.result
ausgeführt werden, welches an diesem Punkt verfügbar ist.
onerror
1// Fired when database fails to open
2request.onerror = (event) => {
3 console.error('Failed to open database:', event.target.error);
4};
- Das
onerror
-Ereignis wird ausgelöst, wenn das Öffnen der Datenbank fehlschlägt. Hier sollten Fehler protokolliert und behandelt werden.
onupgradeneeded
1// Fired when database is newly created or upgraded
2request.onupgradeneeded = (event) => {
3 const db = event.target.result;
4 console.log('Database upgrade needed (or newly created):', db.name);
5
6 // Example: Create an object store (like a table) if it doesn’t exist
7 if (!db.objectStoreNames.contains('users')) {
8 db.createObjectStore('users', { keyPath: 'id' });
9 console.log('Object store "users" created');
10 }
11};
onupgradeneeded
wird ausgelöst, wenn die Datenbank neu erstellt wurde oder die angegebene Version höher als die aktuelle Version ist. Das Erstellen von Tabellen (Object Stores) und das Definieren des Schemas sollte in diesem Moment erfolgen.
Erstellen eines Object Stores
Zuerst wird im onupgradeneeded
-Ereignis ein 'Object Store' (vergleichbar mit einer Tabelle) erstellt.
1request.onupgradeneeded = function (event) {
2 const db = event.target.result;
3 console.log("onupgradeneeded triggered. Database version:", db.version);
4 if (!db.objectStoreNames.contains('users')) {
5 console.log("Creating object store: users");
6 const store = db.createObjectStore('users', { keyPath: 'id' });
7 store.createIndex('name', 'name', { unique: false });
8 }
9};
Hier werden die folgenden Einstellungen vorgenommen:.
-
createObjectStore
createObjectStore
ist eine Methode zum Erstellen eines neuen Object Stores in der Datenbank. In einem Object Store können Sie Datensätze speichern und über Optionen wiekeyPath
verwalten, wie die Daten gespeichert werden. Dieser Vorgang muss innerhalb desonupgradeneeded
Ereignisses durchgeführt werden. -
keyPath: 'id'
Setzen SiekeyPath
auf die Eigenschaftid
, damit jeder Datensatz eindeutig durch einen Primärschlüssel identifiziert wird. So kannid
automatisch beim Hinzufügen, Suchen oder Aktualisieren von Daten verwendet werden. -
createIndex
Verwenden Sie die MethodecreateIndex
, um für Suchzwecke einen Index auf der Eigenschaftname
zu erstellen. Durch das Setzen vonunique: false
sind mehrere Datensätze mit demselbenname
erlaubt.
Umgang mit erfolgreichen Datenbankverbindungen
Weisen Sie die auszuführenden Prozesse bei einer erfolgreichen Datenbankverbindung dem onsuccess
-Ereignis zu. In diesem Prozess erhalten Sie die Datenbankinstanz und bereiten das Lesen und Schreiben von Daten vor.
1request.onsuccess = function (event) {
2 const db = event.target.result;
3 console.log('Database opened successfully');
4 // Use db for reading and writing in subsequent operations
5};
-
Dieser Prozess wird ausgeführt, wenn die Verbindung zur
IndexedDB
-Datenbank erfolgreich hergestellt wurde. -
event.target.result
enthält die geöffnete Datenbankinstanz (IDBDatabase
Objekt), mit der Transaktionen gestartet und Object Stores angesprochen werden können. -
Tatsächliche Lese- und Schreiboperationen wie das Hinzufügen, Abrufen, Aktualisieren und Löschen von Daten werden mit dem
db
-Objekt ausgeführt.
An diesem Punkt ist die Datenbank bereit, sodass Sie sicher Transaktionen starten können.
Daten hinzufügen
So fügen Sie neue Daten zu IndexedDB
hinzu:.
1function addUser(db, user) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.add(user);
5
6 request.onsuccess = () => {
7 console.log('User added:', user);
8 };
9
10 request.onerror = () => {
11 console.error('Add failed:', request.error);
12 };
13}
14
15// Example: Add a user
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 addUser(db, { id: 1, name: 'Alice' });
19 addUser(db, { id: 2, name: 'Bob' });
20 addUser(db, { id: 3, name: 'John' });
21};
- Erstellen Sie eine Transaktion mit
db.transaction()
und geben Sie den Object Store sowie den Modus an (in diesem Fallreadwrite
). - Fügen Sie neue Daten mit der Methode
store.add()
hinzu. - Transaktionen werden automatisch abgeschlossen, aber wenn Sie mehrere Operationen gruppieren möchten, können Sie diese mit Transaktions-Endereignissen steuern.
Daten abrufen (Primärschlüssel-Suche)
So rufen Sie mit dem Primärschlüssel gezielt Daten ab:.
1function getUserById(db, id) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.get(id);
5
6 request.onsuccess = () => {
7 if (request.result) {
8 console.log('User found:', request.result);
9 } else {
10 console.log('User not found');
11 }
12 };
13
14 request.onerror = () => {
15 console.error('Error retrieving user:', request.error);
16 };
17}
18
19// Example: Get a user by id
20request.onsuccess = function (event) {
21 const db = event.target.result;
22 getUserById(db, 1);
23};
- Erstellen Sie eine schreibgeschützte Transaktion mit
db.transaction()
. - Verwenden Sie
store.get(id)
, um die zum angegebenen Primärschlüssel gehörenden Daten abzurufen. onsuccess
wird aufgerufen, wenn der Abruf erfolgreich war. Das Ergebnis wird angezeigt, falls vorhanden. Gibt es kein Ergebnis, gilt dies als 'keine Entsprechung gefunden'.
Suche per Index
Wenn Sie nach anderen Eigenschaften als dem Primärschlüssel suchen möchten, verwenden Sie den zuvor erstellten Index.
1function getUserByName(db, name) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.get(name);
6
7 request.onsuccess = () => {
8 if (request.result) {
9 console.log('User by name:', request.result);
10 } else {
11 console.log('No user found with that name');
12 }
13 };
14
15 request.onerror = () => {
16 console.error('Error retrieving user by name:', request.error);
17 };
18}
19
20// Example: Get a user by id
21request.onsuccess = function (event) {
22 const db = event.target.result;
23 getUserByName(db, 'Alice');
24};
- Greifen Sie mit
store.index('name')
auf den bereits erstelltenname
-Index zu. index.get(value)
gibt den ersten Datensatz mit dem passenden Wert zurück. Wenn mehrere Datensätze denselben Wert haben, können Sie mitindex.getAll(value)
alle davon abrufen.
Daten aktualisieren
Um vorhandene Daten zu aktualisieren (überschreiben), verwenden Sie die Methode put()
.
Existiert ein Datensatz mit passendem Primärschlüssel, wird er aktualisiert. Andernfalls wird ein neuer Datensatz angelegt.
1function updateUser(db, updatedUser) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.put(updatedUser);
5
6 request.onsuccess = () => {
7 console.log('User updated:', updatedUser);
8 };
9
10 request.onerror = () => {
11 console.error('Update failed:', request.error);
12 };
13}
14
15// Example: Update user
16request.onsuccess = async (event) => {
17 const db = event.target.result;
18
19 // Test : update existing user
20 updateUser(db, { id: 3, name: 'John Updated' });
21
22 // Test : insert new user
23 updateUser(db, { id: 4, name: 'Charlie' });
24};
put()
ist eine praktische Methode, die sowohl das Aktualisieren als auch das Hinzufügen unterstützt.- Wenn bereits Daten mit demselben Primärschlüssel existieren, werden diese überschrieben.
- Wenn Sie vor dem Aktualisieren das Vorhandensein prüfen möchten, können Sie dies mit
get()
vorher bestätigen.
Daten löschen
Um Daten zum angegebenen Primärschlüssel zu löschen, verwenden Sie die Methode delete()
.
1function deleteUser(db, id) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const request = store.delete(id);
5
6 request.onsuccess = () => {
7 console.log(`User with id=${id} deleted successfully`);
8 };
9
10 request.onerror = () => {
11 console.error(`Failed to delete user with id=${id}:`, request.error);
12 };
13}
14
15// Example: Delete a user by id
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 deleteUser(db, 4);
19};
- Verwenden Sie
store.delete(id)
, um Daten mit übereinstimmendem Primärschlüssel zu löschen. - Auch wenn die Daten nicht existieren, tritt kein Fehler auf und der Vorgang gilt als erfolgreich.
- Durch die Implementierung einer Fehlerbehandlung wird Ihr Code robuster.
Alle Daten abrufen
getAll()
Um alle Datensätze eines Object Stores abzurufen, verwenden Sie die Methode getAll()
.
1function getAllUsers(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.getAll();
5
6 request.onsuccess = () => {
7 console.log('All users:', request.result);
8 };
9
10 request.onerror = () => {
11 console.error('Failed to retrieve users:', request.error);
12 };
13}
14
15// Example: Get all users
16request.onsuccess = function (event) {
17 const db = event.target.result;
18 getAllUsers(db);
19};
getAll()
ruft alle Datensätze des angegebenen Object Stores als Array ab.- Auch beim Abrufen großer Datenmengen auf einmal erfolgt die Verarbeitung effizient.
- Die Ergebnisse werden als Array in
request.result
gespeichert. - Fügen Sie immer eine Fehlerbehandlung hinzu, um Fehler abfangen zu können.
openCursor()
openCursor()
ist eine Methode für das sequenzielle Durchlaufen von Datensätzen in einem Object Store oder Index. Sie ist nützlich, wenn Sie Daten einzeln verarbeiten möchten, anstatt große Mengen auf einmal abzurufen.
1function getAllUsersWithCursor(db) {
2 const transaction = db.transaction('users', 'readonly');
3 const store = transaction.objectStore('users');
4 const request = store.openCursor();
5
6 request.onsuccess = () => {
7 const cursor = request.result;
8 if (cursor) {
9 console.log('User:', cursor.value); // Process the current record
10 cursor.continue(); // Move to the next record
11 } else {
12 console.log('All users have been processed.');
13 }
14 };
15
16 request.onerror = () => {
17 console.error('Failed to open cursor:', request.error);
18 };
19}
20
21// Example: Get all users
22request.onsuccess = function (event) {
23 const db = event.target.result;
24 getAllUsersWithCursor(db);
25};
- Mit
openCursor()
starten Sie einen Cursor und rufen alle Datensätze im Object Store einzeln ab. - Mit
cursor.value
erhalten Sie das Datenobjekt des aktuellen Datensatzes. - Bewegen Sie den Cursor mit
cursor.continue()
zum nächsten Datensatz. - Wenn
cursor === null
ist, wurden alle Datensätze durchlaufen.
Beispiel für einen Aktualisierungsvorgang mit openCursor()
Zum Beispiel sieht der Vorgang, um den Namen eines Benutzers mit name
Alice
zu Alicia
zu ändern, so aus:.
1function updateUserName(db, oldName, newName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name'); // Use the 'name' index
5 const request = index.openCursor(IDBKeyRange.only(oldName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const user = cursor.value;
11 user.name = newName; // Update the name
12 const updateRequest = cursor.update(user);
13
14 updateRequest.onsuccess = () => {
15 console.log('Updated user:', user);
16 };
17
18 updateRequest.onerror = () => {
19 console.error('Failed to update user:', updateRequest.error);
20 };
21
22 cursor.continue();
23 } else {
24 console.log('All matching users have been updated.');
25 }
26 };
27
28 request.onerror = () => {
29 console.error('Cursor error:', request.error);
30 };
31}
32
33// Example: Update user name
34request.onsuccess = function (event) {
35 const db = event.target.result;
36 updateUserName(db, 'Alice', 'Alicia');
37};
-
IDBKeyRange.only(oldName)
MitIDBKeyRange.only
können Sie nur die Datensätze auswählen, deren Schlüssel genau mitoldName
übereinstimmt. Dies ist nützlich, wenn Sie direkt auf einen bestimmten Wert zugreifen möchten. -
cursor.update()
Nachdemcursor.value
aktualisiert wurde, überschreibt ein Aufruf vonupdate()
den entsprechenden Datensatz. -
Verarbeitung mehrerer Übereinstimmungen Mit
cursor.continue()
können Sie den Cursor zum nächsten passenden Datensatz bewegen. So können Sie mehrere Datensätze mit demselben Schlüssel oder derselben Bedingung nacheinander verarbeiten. -
Fehlerbehandlung Durch das Ausgeben von Logs im
onerror
-Fall wird es einfacher, Ursachen zu untersuchen und während des Betriebs Fehler zu beheben.
Beispiel für einen Löschvorgang mit openCursor()
Zum Beispiel sieht der Vorgang, alle Benutzer mit dem name
Bob
zu löschen, so aus:.
1function deleteUsersByName(db, targetName) {
2 const transaction = db.transaction('users', 'readwrite');
3 const store = transaction.objectStore('users');
4 const index = store.index('name');
5 const request = index.openCursor(IDBKeyRange.only(targetName));
6
7 request.onsuccess = () => {
8 const cursor = request.result;
9 if (cursor) {
10 const deleteRequest = cursor.delete();
11
12 deleteRequest.onsuccess = () => {
13 console.log('Deleted user:', cursor.value);
14 };
15
16 deleteRequest.onerror = () => {
17 console.error('Failed to delete user:', deleteRequest.error);
18 };
19
20 cursor.continue();
21 } else {
22 console.log('All matching users have been deleted.');
23 }
24 };
25
26 request.onerror = () => {
27 console.error('Cursor error:', request.error);
28 };
29}
30
31// Example: Delete user by name
32request.onsuccess = function (event) {
33 const db = event.target.result;
34 deleteUsersByName(db, 'Bob');
35};
cursor.delete()
Mitcursor.delete()
wird der Datensatz an der aktuellen Cursor-Position gelöscht. Da das Ergebnis asynchron zurückgegeben wird, können Sie den Vorgang inonsuccess
überprüfen.
Hinweise zu Transaktionen und asynchroner Verarbeitung
IndexedDB
ist asynchron und ereignisgesteuert. Alle Vorgänge müssen mit onsuccess
- oder onerror
-Ereignissen behandelt werden. Wenn Sie mehrere Vorgänge bündeln möchten, ist es praktisch, diese in ein Promise
zu kapseln.
1function openDatabase() {
2 return new Promise((resolve, reject) => {
3 const request = indexedDB.open('MyDatabase', 1);
4
5 request.onupgradeneeded = function (event) {
6 const db = event.target.result;
7
8 // Initialization process (creating object stores and indexes, etc.)
9 const store = db.createObjectStore('users', { keyPath: 'id' });
10 store.createIndex('name', 'name', { unique: false });
11 };
12
13 request.onsuccess = function (event) {
14 const db = event.target.result;
15 resolve(db);
16 };
17
18 request.onerror = function () {
19 reject(request.error);
20 };
21 });
22}
23
24function addUserAsync(db, user) {
25 return new Promise((resolve, reject) => {
26 const transaction = db.transaction('users', 'readwrite');
27 const store = transaction.objectStore('users');
28 const request = store.add(user);
29
30 request.onsuccess = () => resolve();
31 request.onerror = () => reject(request.error);
32 });
33}
34
35async function main() {
36 try {
37 const db = await openDatabase();
38 await addUserAsync(db, { id: 1, name: 'Alice' });
39 console.log('User added successfully');
40 } catch (error) {
41 console.error('Error:', error);
42 }
43}
- Wenn Sie Funktionen wie
openDatabase
oderaddUserAsync
mit einemPromise
umhüllen, können Sie asynchrone Vorgänge intuitiv mitasync/await
behandeln. - Dadurch wird das sogenannte Callback-Hell vermieden und der Code wird lesbarer.
Zusammenfassung
IndexedDB
ist eine sehr leistungsfähige Funktion, wenn Sie fortgeschrittenes Datenmanagement auf der Browser-Seite durchführen möchten. Anfangs kann die ereignisbasierte asynchrone Verarbeitung verwirrend sein, aber sobald Sie die Struktur verstehen, können Sie umfassende Datenoperationen auf der Clientseite durchführen.
Insbesondere das Beachten der folgenden Punkte hilft, die Nutzung zu erleichtern:.
- Führen Sie die Ersteinrichtung mit
onupgradeneeded
durch. - Achten Sie auf den Lese-/Schreibmodus von Transaktionen.
- Indizes ermöglichen effiziente Suchvorgänge.
- Daten können als Objekte gespeichert werden, was eine hohe Kompatibilität mit JSON gewährleistet.
Wenn Sie IndexedDB
beherrschen, wird das Datenmanagement für PWAs und Offline-Apps erheblich erleichtert.
Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.