Basic concepts and usage of IndexedDB
IndexedDB is a browser-side database provided by HTML5, suitable for storing large amounts of structured data. It is based on key-value storage, supports transaction processing, can work offline, and is ideal for managing data in complex web applications.
Core Concepts of IndexedDB
The core concepts of IndexedDB include databases, object stores, indexes, and transactions. A database is the highest-level container, with each database containing multiple object stores. Object stores are similar to tables in relational databases and are used to store actual data. Indexes enable fast data queries, while transactions ensure the atomicity of data operations.
Databases use a version control mechanism, where every structural change requires a version number upgrade. For example, when creating a new object store or modifying an existing structure, the change must be implemented through a version upgrade:
const request = indexedDB.open('myDatabase', 2);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('users')) {
db.createObjectStore('users', { keyPath: 'id' });
}
};
Opening and Closing Databases
To open a database, use the indexedDB.open()
method, which returns an IDBRequest
object. The first parameter is the database name, and the second optional parameter is the version number. If the database does not exist, it will be created automatically.
const openRequest = indexedDB.open('myDatabase', 1);
openRequest.onerror = (event) => {
console.error('Failed to open database:', event.target.error);
};
openRequest.onsuccess = (event) => {
const db = event.target.result;
console.log('Database opened successfully');
// Close the connection after use
db.close();
};
Note that closing the database connection is important, especially in single-page applications (SPAs), to avoid memory leaks.
Object Store Operations
Object stores are the basic units of data storage in IndexedDB. Creating an object store can only be done within the onupgradeneeded
event. During creation, you can specify primary keys and auto-increment options:
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Create an object store with 'id' as the primary key
const store = db.createObjectStore('books', {
keyPath: 'id',
autoIncrement: true
});
// Create indexes
store.createIndex('by_title', 'title', { unique: false });
store.createIndex('by_author', 'author', { unique: false });
};
CRUD Operations
All data operations in IndexedDB must be performed within transactions. Transactions come in two modes: read-only and read-write.
Example of adding data:
const transaction = db.transaction(['books'], 'readwrite');
const store = transaction.objectStore('books');
const book = {
title: 'JavaScript: The Definitive Guide',
author: 'Nicholas C. Zakas',
year: 2020
};
const request = store.add(book);
request.onsuccess = () => {
console.log('Data added successfully');
};
Data can be queried using primary keys or indexes:
const transaction = db.transaction(['books'], 'readonly');
const store = transaction.objectStore('books');
const index = store.index('by_title');
const request = index.get('JavaScript: The Definitive Guide');
request.onsuccess = (event) => {
const book = event.target.result;
console.log(book);
};
Transaction Handling
IndexedDB transactions are auto-committed and do not require manual commit calls. However, it is important to note the transaction lifecycle:
const transaction = db.transaction(['books'], 'readwrite');
const store = transaction.objectStore('books');
// The transaction automatically times out after 60 seconds
transaction.oncomplete = () => {
console.log('Transaction completed');
};
transaction.onerror = (event) => {
console.error('Transaction error:', event.target.error);
};
// All operations must be completed while the transaction is active
setTimeout(() => {
// This will cause an error because the transaction may have already ended
store.add({ title: 'Book added after timeout' });
}, 1000);
Cursor Iteration
For traversing large datasets, cursors are used:
const transaction = db.transaction(['books'], 'readonly');
const store = transaction.objectStore('books');
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.key, cursor.value);
cursor.continue();
} else {
console.log('Iteration complete');
}
};
Cursors can be combined with indexes for range queries:
const range = IDBKeyRange.bound('A', 'M');
const index = store.index('by_title');
const request = index.openCursor(range);
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.value.title);
cursor.continue();
}
};
Performance Optimization
When using IndexedDB, consider the following performance tips:
- Batch operations using transaction merging
- Use indexes wisely to speed up queries
- Avoid time-consuming operations within transactions
- Process large data in chunks
Example of batch writing:
function addMultipleBooks(db, books) {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['books'], 'readwrite');
const store = transaction.objectStore('books');
transaction.oncomplete = () => resolve();
transaction.onerror = (event) => reject(event.target.error);
books.forEach(book => {
store.add(book);
});
});
}
Error Handling
Errors in IndexedDB operations should be handled properly:
const request = indexedDB.open('myDatabase');
request.onerror = (event) => {
const error = event.target.error;
if (error.name === 'VersionError') {
console.error('Version number is lower than the current version');
} else if (error.name === 'QuotaExceededError') {
console.error('Insufficient storage space');
} else {
console.error('Database error:', error);
}
};
Practical Use Cases
IndexedDB is suitable for the following scenarios:
- Offline web application data storage
- Client-side caching of large datasets
- Local data requiring complex queries
- Data operations requiring transaction support
For example, implementing an offline notes application:
class NotesDB {
constructor() {
this.dbPromise = new Promise((resolve, reject) => {
const request = indexedDB.open('NotesDB', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('notes')) {
const store = db.createObjectStore('notes', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('by_date', 'createdAt', { unique: false });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async addNote(note) {
const db = await this.dbPromise;
return new Promise((resolve, reject) => {
const transaction = db.transaction(['notes'], 'readwrite');
const store = transaction.objectStore('notes');
const request = store.add({
...note,
createdAt: new Date()
});
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getNotes() {
const db = await this.dbPromise;
return new Promise((resolve, reject) => {
const transaction = db.transaction(['notes'], 'readonly');
const store = transaction.objectStore('notes');
const index = store.index('by_date');
const request = index.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
}
Browser Compatibility
Most modern browsers support IndexedDB, but note the following:
- IE10+ supports it but with a different prefix (
msIndexedDB
) - Mobile browsers generally have good support
- Private browsing modes may impose limitations
Check browser support:
if (!window.indexedDB) {
console.warn('Current browser does not support IndexedDB');
// Consider fallback options like localStorage
}
Debugging Tools
Browser developer tools provide IndexedDB debugging features:
- Chrome DevTools' Application panel
- Firefox's Storage Inspector
- Edge's IndexedDB viewer
These tools allow you to inspect database structures, stored content, and perform simple queries.
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn