مقدمه
با وجودی که Web Storage برای ذخیرهسازی مقادیر ساده و کمحجم عالی است، اما زمانی که با حجم
زیادی از دادههای ساختاریافته سروکار داریم یا به قابلیتهای جستجوی پیشرفته نیاز داریم، به یک
راهحل قدرتمندتر نیازمندیم. IndexedDB یک پایگاه داده کامل و شیءگرا در سمت کلاینت است که توسط
مرورگرها ارائه میشود. این API به ما اجازه میدهد تا حجم بسیار زیادی از داده (حتی فایلها و
Blobها) را ذخیره کرده، برای جستجوی سریع روی آنها ایندکس بسازیم، و تمام عملیات را در قالب
تراکنشها (transactions) برای تضمین یکپارچگی دادهها انجام دهیم.
گردش کار با IndexedDB
API خام IndexedDB کمی پرجزئیات و مبتنی بر رویداد است. بیایید با یک مثال کامل، مراحل اصلی باز
کردن پایگاهداده، ایجاد schema، و افزودن و خواندن داده را بررسی کنیم.
۱. باز کردن اتصال به پایگاهداده
اولین قدم، باز کردن یک اتصال به پایگاهداده با indexedDB.open(dbName,
version) است. این عملیات ناهمزمان است و یک شیء request برمیگرداند که باید به
رویدادهای success، error و upgradeneeded آن گوش دهیم.
JAVASCRIPT
let db;
const request = indexedDB.open('MyNotesDatabase', 1);
request.onupgradeneeded = (event) => {
db = event.target.result;
const objectStore = db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
objectStore.createIndex('title', 'title', { unique: false });
};
request.onsuccess = (event) => {
db = event.target.result;
console.log('Database opened successfully.');
};
request.onerror = (event) => {
console.error('Database error:', event.target.error);
};
مهمترین بخش این کد، رویداد onupgradeneeded است. این رویداد تنها جایی است که میتوانیم
ساختار پایگاهداده خود را تغییر دهیم (مثلاً آبجکت استورها و ایندکسهای جدیدی بسازیم). این رویداد
زمانی اجرا میشود که پایگاهداده برای اولین بار ایجاد میشود یا زمانی که شماره نسخهای که در
open پاس میدهیم، از نسخه فعلی پایگاهداده بیشتر باشد.
۲. افزودن و خواندن داده (تراکنشها)
پس از برقراری اتصال، تمام عملیات داده باید داخل یک تراکنش انجام شود. برای شروع یک تراکنش، از متد
db.transaction() استفاده میکنیم که نام آبجکت استور(های) مورد نظر و
نوع تراکنش ('readonly' یا 'readwrite') را به عنوان آرگومان میگیرد.
JAVASCRIPT
function addNote(noteData) {
const transaction = db.transaction(['notes'], 'readwrite');
const objectStore = transaction.objectStore('notes');
const request = objectStore.add(noteData);
request.onsuccess = () => console.log('Note added successfully!');
request.onerror = (e) => console.error('Error adding note:', e.target.error);
}
function getNote(noteId) {
const transaction = db.transaction(['notes'], 'readonly');
const objectStore = transaction.objectStore('notes');
const request = objectStore.get(noteId);
request.onsuccess = () => console.log('Found note:', request.result);
}
این توابع کمکی، گردش کار یک تراکنش را نشان میدهند: ۱. شروع تراکنش، ۲. گرفتن دسترسی به آبجکت
استور، ۳. اجرای عملیات (مانند add یا get). هر یک از این عملیات نیز خود یک شیء request
برمیگرداند که باید به رویدادهای success و error آن گوش دهیم.
چالش ناهمزمانی و کتابخانههای کمکی
همانطور که مشاهده کردید، API خام IndexedDB بسیار پرجزئیات و مبتنی بر شنوندگان رویداد است که
میتواند منجر به کدهای تودرتو و پیچیده (معروف به "Callback Hell") شود. به همین دلیل، بسیاری از
توسعهدهندگان ترجیح میدهند از کتابخانههای کمکی (wrapper libraries) کوچک استفاده کنند.
این کتابخانهها، API مبتنی بر رویداد IndexedDB را به یک API مبتنی بر Promise تبدیل میکنند
که کار با آن، به خصوص با استفاده از async/await، بسیار سادهتر و خواناتر میشود. یکی از
معروفترین این کتابخانهها، idb نوشته Jake Archibald است.
در این درس با IndexedDB به عنوان قدرتمندترین راهحل ذخیرهسازی سمت کلاینت آشنا شدیم. دیدیم که
با وجود API پیچیده و ناهمزمان، این تکنولوژی امکانات یک پایگاه داده واقعی را برای ساخت وب
اپلیکیشنهای آفلاین و غنی از داده فراهم میکند. با این درس، فصل «ذخیرهسازی دادهها در سمت
کاربر» به پایان میرسد. ما از کوکیهای ساده شروع کردیم، با Web Storage ادامه دادیم و با
پایگاهداده قدرتمند IndexedDB کار را به اتمام رساندیم. در فصل بعدی، به سراغ «کار با
ماژولها» خواهیم رفت و یاد میگیریم که چگونه کدهای جاوااسکریپت خود را برای پروژههای بزرگ، به
صورت ماژولار و قابل مدیریت سازماندهی کنیم.