این راهنما اصول IndexedDB API را پوشش می دهد. ما از کتابخانه Jake Archibald's IndexedDB Promised استفاده میکنیم، که بسیار شبیه به IndexedDB API است، اما از قولهایی استفاده میکند که میتوانید برای نحو مختصرتر await
. این API را ساده می کند و در عین حال ساختار آن را حفظ می کند.
IndexedDB چیست؟
IndexedDB یک سیستم ذخیره سازی NoSQL در مقیاس بزرگ است که تقریباً هر چیزی را در مرورگر کاربر ذخیره می کند. IndexedDB علاوه بر اقدامات معمول جستجو، دریافت و قرار دادن، از تراکنش ها نیز پشتیبانی می کند و برای ذخیره حجم زیادی از داده های ساختاریافته مناسب است.
هر پایگاه داده IndexedDB برای یک مبدا (معمولاً دامنه یا زیر دامنه سایت) منحصر به فرد است، به این معنی که نمی تواند به هیچ منبع دیگری دسترسی داشته باشد یا به آن دسترسی پیدا کند. محدودیتهای ذخیرهسازی داده آن معمولاً زیاد است، اگر اصلاً وجود داشته باشد، اما مرورگرهای مختلف محدودیتها و حذف دادهها را متفاوت مدیریت میکنند. برای اطلاعات بیشتر به بخش مطالعه بیشتر مراجعه کنید.
شرایط IndexedDB
- پایگاه داده
- بالاترین سطح IndexedDB. این شامل ذخایر شی است که به نوبه خود حاوی داده هایی است که می خواهید باقی بماند. می توانید چندین پایگاه داده با هر نامی که انتخاب می کنید ایجاد کنید.
- فروشگاه اشیاء
- یک سطل جداگانه برای ذخیره داده ها، مشابه جداول در پایگاه داده های رابطه ای. به طور معمول، برای هر نوع داده ای که ذخیره می کنید (و نه نوع داده جاوا اسکریپت) یک شی ذخیره می شود. برخلاف جداول پایگاه داده، انواع داده جاوا اسکریپت در یک فروشگاه نیازی به سازگاری ندارند. برای مثال، اگر یک برنامه دارای یک فروشگاه شی
people
حاوی اطلاعات مربوط به سه نفر باشد، ویژگیهای سنی آن افراد میتواند53
،'twenty-five'
وunknown
باشد. - شاخص
- نوعی ذخیرهسازی شی برای سازماندهی دادهها در یک ذخیرهگاه شی دیگر (به نام ذخیره شی مرجع) توسط یک ویژگی جداگانه از دادهها. ایندکس برای بازیابی رکوردها در شی ذخیره توسط این ویژگی استفاده می شود. برای مثال، اگر افراد را ذخیره میکنید، ممکن است بخواهید بعداً آنها را با نام، سن یا حیوان مورد علاقهشان بیاورید.
- عملیات
- تعامل با پایگاه داده
- معامله
- یک پوشش در اطراف یک عملیات یا گروهی از عملیات که یکپارچگی پایگاه داده را تضمین می کند. اگر یکی از اقدامات در یک تراکنش با شکست مواجه شود، هیچ یک از آنها اعمال نمی شود و پایگاه داده به حالت قبل از شروع تراکنش برمی گردد. تمام عملیات خواندن یا نوشتن در IndexedDB باید بخشی از یک تراکنش باشد. این اجازه می دهد تا عملیات خواندن-تغییر-نوشتن اتمی بدون خطر درگیری با رشته های دیگر که همزمان بر روی پایگاه داده عمل می کنند.
- مکان نما
- مکانیزمی برای تکرار روی چندین رکورد در یک پایگاه داده.
چگونه پشتیبانی از IndexedDB را بررسی کنیم
IndexedDB تقریباً به طور جهانی پشتیبانی می شود. با این حال، اگر با مرورگرهای قدیمیتر کار میکنید، ایده بدی نیست که پشتیبانی از ویژگیها را در هر صورت شناسایی کنید. ساده ترین راه این است که شی window
را بررسی کنید:
function indexedDBStuff () {
// Check for IndexedDB support:
if (!('indexedDB' in window)) {
// Can't use IndexedDB
console.log("This browser doesn't support IndexedDB");
return;
} else {
// Do IndexedDB stuff here:
// ...
}
}
// Run IndexedDB code:
indexedDBStuff();
نحوه باز کردن پایگاه داده
با IndexedDB، می توانید چندین پایگاه داده با هر نامی که انتخاب می کنید ایجاد کنید. اگر زمانی که میخواهید آن را باز کنید پایگاه داده وجود نداشته باشد، به طور خودکار ایجاد میشود. برای باز کردن پایگاه داده، از متد openDB()
از کتابخانه idb
استفاده کنید:
import {openDB} from 'idb';
async function useDB () {
// Returns a promise, which makes `idb` usable with async-await.
const dbPromise = await openDB('example-database', version, events);
}
useDB();
این روش یک وعده را برمی گرداند که به یک شی پایگاه داده حل می شود. هنگام استفاده از متد openDB()
، یک نام، شماره نسخه و یک شی رویداد برای راه اندازی پایگاه داده ارائه کنید.
در اینجا مثالی از متد openDB()
در متن آورده شده است:
import {openDB} from 'idb';
async function useDB () {
// Opens the first version of the 'test-db1' database.
// If the database does not exist, it will be created.
const dbPromise = await openDB('test-db1', 1);
}
useDB();
چک پشتیبانی IndexedDB را در بالای تابع ناشناس قرار دهید. اگر مرورگر از IndexedDB پشتیبانی نکند، از عملکرد خارج می شود. اگر تابع بتواند ادامه دهد، متد openDB()
را برای باز کردن پایگاه داده ای به نام 'test-db1'
فراخوانی می کند. در این مثال، شی رویدادهای اختیاری برای ساده نگه داشتن چیزها کنار گذاشته شده است، اما شما باید آن را برای انجام هر کار معناداری با IndexedDB مشخص کنید.
نحوه کار با شی فروشگاه
یک پایگاه داده IndexedDB حاوی یک یا چند شی ذخیره است که هر کدام یک ستون برای یک کلید و ستون دیگری برای داده های مرتبط با آن کلید دارند.
ایجاد فروشگاه های شی
یک پایگاه داده IndexedDB با ساختار مناسب باید برای هر نوع داده ای که باید حفظ شود، یک شی ذخیره داشته باشد. برای مثال، سایتی که نمایهها و یادداشتهای کاربر را حفظ میکند، ممکن است دارای یک فروشگاه شی people
حاوی اشیاء person
و یک ذخیرهسازی شی notes
حاوی اشیاء note
باشد.
برای اطمینان از یکپارچگی پایگاه داده، فقط میتوانید در یک فراخوانی openDB()
ذخیرههای شی را در شی رویداد ایجاد یا حذف کنید. شی رویداد یک متد upgrade()
را نشان می دهد که به شما امکان می دهد فروشگاه های شی ایجاد کنید. متد createObjectStore()
در متد upgrade()
فراخوانی کنید تا فروشگاه شی ایجاد شود:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('example-database', 1, {
upgrade (db) {
// Creates an object store:
db.createObjectStore('storeName', options);
}
});
}
createStoreInDB();
این روش نام شی ذخیره و یک شی پیکربندی اختیاری را می گیرد که به شما امکان می دهد خصوصیات مختلفی را برای ذخیره شی تعریف کنید.
در زیر نمونه ای از نحوه استفاده از createObjectStore()
آورده شده است:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db1', 1, {
upgrade (db) {
console.log('Creating a new object store...');
// Checks if the object store exists:
if (!db.objectStoreNames.contains('people')) {
// If the object store does not exist, create it:
db.createObjectStore('people');
}
}
});
}
createStoreInDB();
در این مثال، یک آبجکت رویداد به متد openDB()
داده می شود تا ذخیره شیء ایجاد شود و مانند قبل، کار ایجاد ذخیره شی در متد upgrade()
شی رویداد انجام می شود. با این حال، از آنجایی که اگر بخواهید یک شی ذخیرهسازی ایجاد کنید، مرورگر با خطا مواجه میشود، توصیه میکنیم متد createObjectStore()
را در یک دستور if
قرار دهید که بررسی میکند آیا ذخیره شی وجود دارد یا خیر. در داخل بلوک if
، createObjectStore()
را فراخوانی کنید تا یک ذخیره شی با نام 'firstOS'
ایجاد کنید.
نحوه تعریف کلیدهای اولیه
هنگامی که ذخیرههای شی را تعریف میکنید، میتوانید نحوه شناسایی منحصربهفرد دادهها در فروشگاه را با استفاده از کلید اصلی تعریف کنید. شما می توانید یک کلید اصلی را با تعریف یک مسیر کلید یا با استفاده از یک مولد کلید تعریف کنید.
مسیر کلیدی خاصیتی است که همیشه وجود دارد و حاوی یک مقدار منحصر به فرد است. به عنوان مثال، در مورد یک فروشگاه اشیاء people
، ممکن است آدرس ایمیل را به عنوان مسیر کلید انتخاب کنید:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
db.createObjectStore('people', { keyPath: 'email' });
}
}
});
}
createStoreInDB();
این مثال یک ذخیره شی به نام 'people'
ایجاد می کند و ویژگی email
را به عنوان کلید اصلی در گزینه keyPath
اختصاص می دهد.
همچنین می توانید از یک مولد کلید مانند autoIncrement
استفاده کنید. مولد کلید یک مقدار منحصر به فرد برای هر شی که به ذخیره شی اضافه می شود ایجاد می کند. به طور پیش فرض، اگر کلیدی را مشخص نکنید، IndexedDB یک کلید ایجاد می کند و آن را جدا از داده ها ذخیره می کند.
مثال زیر یک ذخیرهسازی شی به نام 'notes'
ایجاد میکند و کلید اصلی را تنظیم میکند تا بهطور خودکار بهعنوان یک عدد افزایشدهنده خودکار اختصاص داده شود:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { autoIncrement: true });
}
}
});
}
createStoreInDB();
مثال زیر مشابه مثال قبلی است، اما این بار مقدار افزایش خودکار به طور صریح به ویژگی با نام 'id'
اختصاص داده شده است.
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('logs')) {
db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createStoreInDB();
انتخاب روشی که برای تعریف کلید استفاده شود به داده های شما بستگی دارد. اگر دادههای شما دارای خاصیتی هستند که همیشه منحصربهفرد است، میتوانید آن را به عنوان keyPath
برای اعمال این منحصربهفرد بودن تبدیل کنید. در غیر این صورت، از یک مقدار افزایش خودکار استفاده کنید.
کد زیر سه فروشگاه شی ایجاد میکند که روشهای مختلف تعریف کلیدهای اولیه در ذخیرهسازی شی را نشان میدهد:
import {openDB} from 'idb';
async function createStoresInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
db.createObjectStore('people', { keyPath: 'email' });
}
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { autoIncrement: true });
}
if (!db.objectStoreNames.contains('logs')) {
db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createStoresInDB();
نحوه تعریف شاخص ها
ایندکسها نوعی ذخیرهسازی شی هستند که برای بازیابی دادهها از ذخیره شی مرجع توسط یک ویژگی مشخص استفاده میشوند. یک نمایه در داخل فروشگاه شی مرجع زندگی می کند و حاوی همان داده است، اما از ویژگی مشخص شده به عنوان مسیر کلید خود به جای کلید اصلی فروشگاه مرجع استفاده می کند. زمانی که ذخیرههای شیء خود را ایجاد میکنید، باید ایندکسها ایجاد شوند و میتوان از آنها برای تعریف یک محدودیت منحصر به فرد بر روی دادههای خود استفاده کرد.
برای ایجاد یک شاخص، متد createIndex()
در یک نمونه ذخیره شیء فراخوانی کنید:
import {openDB} from 'idb';
async function createIndexInStore() {
const dbPromise = await openDB('storeName', 1, {
upgrade (db) {
const objectStore = db.createObjectStore('storeName');
objectStore.createIndex('indexName', 'property', options);
}
});
}
createIndexInStore();
این متد یک شیء شاخص ایجاد و برمی گرداند. متد createIndex()
در نمونه شی ذخیره نام ایندکس جدید را به عنوان آرگومان اول می گیرد و آرگومان دوم به ویژگی روی داده هایی که می خواهید ایندکس کنید اشاره دارد. آرگومان نهایی به شما امکان می دهد دو گزینه را تعریف کنید که نحوه عملکرد شاخص را تعیین می کند: unique
و multiEntry
. اگر unique
روی true
تنظیم شود، ایندکس مقادیر تکراری را برای یک کلید مجاز نمیکند. در مرحله بعد، multiEntry
تعیین میکند که createIndex()
چگونه رفتار میکند زمانی که ویژگی ایندکسشده یک آرایه است. اگر روی true
تنظیم شده باشد، createIndex()
یک ورودی به ایندکس برای هر عنصر آرایه اضافه می کند. در غیر این صورت، یک ورودی واحد حاوی آرایه را اضافه می کند.
در اینجا یک مثال است:
import {openDB} from 'idb';
async function createIndexesInStores () {
const dbPromise = await openDB('test-db3', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
const peopleObjectStore = db.createObjectStore('people', { keyPath: 'email' });
peopleObjectStore.createIndex('gender', 'gender', { unique: false });
peopleObjectStore.createIndex('ssn', 'ssn', { unique: true });
}
if (!db.objectStoreNames.contains('notes')) {
const notesObjectStore = db.createObjectStore('notes', { autoIncrement: true });
notesObjectStore.createIndex('title', 'title', { unique: false });
}
if (!db.objectStoreNames.contains('logs')) {
const logsObjectStore = db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createIndexesInStores();
در این مثال، شیهای ذخیرهسازی 'people'
و 'notes'
دارای فهرست هستند. برای ایجاد ایندکس ها، ابتدا نتیجه createObjectStore()
(یک شی ذخیره شی) را به یک متغیر اختصاص دهید تا بتوانید createIndex()
روی آن فراخوانی کنید.
نحوه کار با داده ها
این بخش نحوه ایجاد، خواندن، به روز رسانی و حذف داده ها را شرح می دهد. این عملیات ها همه ناهمزمان هستند و از وعده هایی استفاده می کنند که در آن API IndexedDB از درخواست ها استفاده می کند. این API را ساده می کند. به جای گوش دادن به رویدادهایی که توسط درخواست ایجاد میشوند، میتوانید .then()
روی شی پایگاه داده بازگشتی از متد openDB()
کنید تا تعامل با پایگاه داده شروع شود یا await
ایجاد آن بمانید.
تمام عملیات داده در IndexedDB در داخل یک تراکنش انجام می شود. هر عملیات دارای فرم زیر است:
- دریافت شی پایگاه داده
- باز کردن تراکنش در پایگاه داده
- فروشگاه شی را در تراکنش باز کنید.
- انجام عملیات در فروشگاه شی.
یک تراکنش را می توان به عنوان یک پوشش امن در اطراف یک عملیات یا گروهی از عملیات در نظر گرفت. اگر یکی از اقدامات در یک تراکنش با شکست مواجه شود، همه اقدامات به عقب برگردانده می شوند. تراکنش ها مختص یک یا چند شی ذخیره هستند که هنگام باز کردن تراکنش، آنها را تعریف می کنید. آنها می توانند فقط خواندنی یا خواندن و نوشتن باشند. این نشان می دهد که آیا عملیات داخل تراکنش داده ها را می خواند یا تغییری در پایگاه داده ایجاد می کند.
داده ایجاد کنید
برای ایجاد داده، متد add()
را در نمونه پایگاه داده فراخوانی کنید و دادههایی را که میخواهید اضافه کنید ارسال کنید. آرگومان اول متد add()
ذخیره شی است که می خواهید داده ها را به آن اضافه کنید و آرگومان دوم یک شی است که حاوی فیلدها و داده های مرتبطی است که می خواهید اضافه کنید. در اینجا ساده ترین مثال است که در آن یک ردیف از داده ها اضافه می شود:
import {openDB} from 'idb';
async function addItemToStore () {
const db = await openDB('example-database', 1);
await db.add('storeName', {
field: 'data'
});
}
addItemToStore();
هر فراخوانی add()
در یک تراکنش اتفاق میافتد، بنابراین حتی اگر وعده با موفقیت حل شود، لزوماً به این معنی نیست که عملیات کار کرده است. برای اطمینان از انجام عملیات add، باید بررسی کنید که آیا کل تراکنش با استفاده از متد transaction.done()
تکمیل شده است یا خیر. این قولی است که با تکمیل تراکنش برطرف میشود و در صورت اشتباه بودن تراکنش رد میشود. شما باید این بررسی را برای تمام عملیات "نوشتن" انجام دهید، زیرا این تنها راه شما برای دانستن اینکه تغییرات در پایگاه داده واقعا اتفاق افتاده است.
کد زیر استفاده از متد add()
را در داخل تراکنش نشان می دهد:
import {openDB} from 'idb';
async function addItemsToStore () {
const db = await openDB('test-db4', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('foods')) {
db.createObjectStore('foods', { keyPath: 'name' });
}
}
});
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Add multiple items to the 'foods' store in a single transaction:
await Promise.all([
tx.store.add({
name: 'Sandwich',
price: 4.99,
description: 'A very tasty sandwich!',
created: new Date().getTime(),
}),
tx.store.add({
name: 'Eggs',
price: 2.99,
description: 'Some nice eggs you can cook up!',
created: new Date().getTime(),
}),
tx.done
]);
}
addItemsToStore();
هنگامی که پایگاه داده را باز کردید (و در صورت نیاز یک فروشگاه شی ایجاد کنید)، باید یک تراکنش را با فراخوانی متد transaction()
روی آن باز کنید. این روش یک آرگومان برای فروشگاهی که میخواهید در آن تراکنش انجام دهید و همچنین حالت میگیرد. در این مورد، ما علاقه مند به نوشتن به فروشگاه هستیم، بنابراین این مثال 'readwrite'
را مشخص می کند.
گام بعدی این است که به عنوان بخشی از تراکنش، اقلام را به فروشگاه اضافه کنید. در مثال قبل، ما با سه عملیات در فروشگاه 'foods'
سروکار داریم که هر کدام یک وعده را برمیگردانند:
- اضافه کردن یک رکورد برای یک ساندویچ خوشمزه.
- افزودن رکورد برای تعدادی تخم مرغ
- نشان دادن این که تراکنش کامل شده است (
tx.done
).
از آنجایی که همه این اقدامات همگی مبتنی بر وعده هستند، باید منتظر تمام شدن همه آنها باشیم. انتقال این وعده ها به Promise.all
یک راه خوب و ارگونومیک برای انجام این کار است. Promise.all
مجموعهای از وعدهها را میپذیرد و زمانی که تمام وعدههای داده شده به آن حل و فصل شد، پایان میدهد.
برای دو رکوردی که اضافه می شوند، رابط store
نمونه تراکنش، add()
فراخوانی می کند و داده ها را به آن ارسال می کند. شما میتوانید await
تماس Promise.all
باشید تا زمانی که تراکنش کامل شود، تماس کامل شود.
داده ها را بخوانید
برای خواندن داده ها، متد get()
را در نمونه پایگاه داده ای که با استفاده از متد openDB()
بازیابی می کنید فراخوانی کنید. get()
نام فروشگاه و مقدار کلید اصلی شی مورد نظر برای بازیابی را می گیرد. در اینجا یک مثال اساسی آورده شده است:
import {openDB} from 'idb';
async function getItemFromStore () {
const db = await openDB('example-database', 1);
// Get a value from the object store by its primary key value:
const value = await db.get('storeName', 'unique-primary-key-value');
}
getItemFromStore();
مانند add()
، متد get()
یک وعده را برمی گرداند، بنابراین در صورت تمایل می توانید await
آن بمانید یا از .then()
callback قول استفاده کنید.
مثال زیر از متد get()
در ذخیرهسازی شیء 'foods'
پایگاه داده 'test-db4'
استفاده میکند تا یک ردیف را با کلید اصلی 'name'
به دست آورد:
import {openDB} from 'idb';
async function getItemFromStore () {
const db = await openDB('test-db4', 1);
const value = await db.get('foods', 'Sandwich');
console.dir(value);
}
getItemFromStore();
بازیابی یک سطر از پایگاه داده نسبتاً ساده است: پایگاه داده را باز کنید و ذخیره شی و مقدار کلید اصلی ردیفی را که می خواهید داده از آن دریافت کنید مشخص کنید. از آنجا که متد get()
یک وعده را برمی گرداند، می توانید await
آن باشید.
به روز رسانی داده ها
برای بهروزرسانی دادهها، متد put()
در فروشگاه شیء فراخوانی کنید. متد put()
شبیه متد add()
است و همچنین می تواند به جای add()
برای ایجاد داده استفاده شود. در اینجا یک مثال اساسی از استفاده از put()
برای به روز رسانی یک ردیف در یک ذخیره شی با مقدار کلید اصلی آن آورده شده است:
import {openDB} from 'idb';
async function updateItemInStore () {
const db = await openDB('example-database', 1);
// Update a value from in an object store with an inline key:
await db.put('storeName', { inlineKeyName: 'newValue' });
// Update a value from in an object store with an out-of-line key.
// In this case, the out-of-line key value is 1, which is the
// auto-incremented value.
await db.put('otherStoreName', { field: 'value' }, 1);
}
updateItemInStore();
مانند روش های دیگر، این روش یک وعده را برمی گرداند. همچنین می توانید put()
به عنوان بخشی از تراکنش استفاده کنید. در اینجا یک مثال با استفاده از فروشگاه 'foods'
از قبل وجود دارد که قیمت ساندویچ و تخم مرغ را به روز می کند:
import {openDB} from 'idb';
async function updateItemsInStore () {
const db = await openDB('test-db4', 1);
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Update multiple items in the 'foods' store in a single transaction:
await Promise.all([
tx.store.put({
name: 'Sandwich',
price: 5.99,
description: 'A MORE tasty sandwich!',
updated: new Date().getTime() // This creates a new field
}),
tx.store.put({
name: 'Eggs',
price: 3.99,
description: 'Some even NICER eggs you can cook up!',
updated: new Date().getTime() // This creates a new field
}),
tx.done
]);
}
updateItemsInStore();
نحوه بهروزرسانی موارد به نحوه تنظیم یک کلید بستگی دارد. اگر یک keyPath
تنظیم کنید، هر ردیف در ذخیرهسازی شی با یک کلید درون خطی مرتبط میشود. مثال قبلی ردیفها را بر اساس این کلید بهروزرسانی میکند، و وقتی ردیفها را در این موقعیت بهروزرسانی میکنید، باید آن کلید را برای بهروزرسانی آیتم مناسب در شیفروشی مشخص کنید. همچنین میتوانید با تنظیم یک autoIncrement
به عنوان کلید اصلی، یک کلید خارج از خط ایجاد کنید.
داده ها را حذف کنید
برای حذف داده ها، متد delete()
را در شی ذخیره کنید:
import {openDB} from 'idb';
async function deleteItemFromStore () {
const db = await openDB('example-database', 1);
// Delete a value
await db.delete('storeName', 'primary-key-value');
}
deleteItemFromStore();
مانند add()
و put()
، می توانید از این به عنوان بخشی از تراکنش استفاده کنید:
import {openDB} from 'idb';
async function deleteItemsFromStore () {
const db = await openDB('test-db4', 1);
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Delete multiple items from the 'foods' store in a single transaction:
await Promise.all([
tx.store.delete('Sandwich'),
tx.store.delete('Eggs'),
tx.done
]);
}
deleteItemsFromStore();
ساختار تعامل پایگاه داده مانند سایر عملیات است. به یاد داشته باشید که با گنجاندن متد tx.done
در آرایه ای که به Promise.all
ارسال می کنید، بررسی کنید که کل تراکنش کامل شده است.
گرفتن تمام داده ها
تاکنون فقط اشیا را یکی یکی از فروشگاه بازیابی کرده اید. همچنین میتوانید تمام دادهها یا یک زیرمجموعه را از یک ذخیره یا فهرست با استفاده از متد getAll()
یا مکاننماها بازیابی کنید.
getAll()
سادهترین راه برای بازیابی تمام دادههای یک شی ذخیرهسازی، فراخوانی getAll()
در ذخیرهسازی شی یا ایندکس است، مانند این:
import {openDB} from 'idb';
async function getAllItemsFromStore () {
const db = await openDB('test-db4', 1);
// Get all values from the designated object store:
const allValues = await db.getAll('storeName');
console.dir(allValues);
}
getAllItemsFromStore();
این متد تمام اشیاء موجود در شی ذخیره را بدون هیچ گونه محدودیتی برمی گرداند. این مستقیم ترین راه برای دریافت همه مقادیر از یک ذخیره شی است، اما کمترین انعطاف را نیز دارد.
import {openDB} from 'idb';
async function getAllItemsFromStore () {
const db = await openDB('test-db4', 1);
// Get all values from the designated object store:
const allValues = await db.getAll('foods');
console.dir(allValues);
}
getAllItemsFromStore();
این مثال getAll()
را در فروشگاه شیء 'foods'
فراخوانی می کند. این همه اشیاء را از 'foods'
که توسط کلید اصلی مرتب شده اند، برمی گرداند.
نحوه استفاده از مکان نما
نشانگرها روشی انعطافپذیرتر برای بازیابی چندین شی هستند. مکان نما هر شیء را در یک ذخیره یا فهرست یک شیء یک به یک انتخاب می کند و به شما این امکان را می دهد که در هنگام انتخاب داده، کاری با داده ها انجام دهید. نشانگرها، مانند سایر عملیات پایگاه داده، در تراکنش ها کار می کنند.
برای ایجاد یک مکان نما، openCursor()
در فروشگاه شی به عنوان بخشی از یک تراکنش فراخوانی کنید. با استفاده از ذخیره 'foods'
از مثالهای قبلی، به این صورت است که مکاننما را در تمام ردیفهای داده در یک ذخیرهسازی شی پیش میبریم:
import {openDB} from 'idb';
async function getAllItemsFromStoreWithCursor () {
const db = await openDB('test-db4', 1);
const tx = await db.transaction('foods', 'readonly');
// Open a cursor on the designated object store:
let cursor = await tx.store.openCursor();
// Iterate on the cursor, row by row:
while (cursor) {
// Show the data in the row at the current cursor position:
console.log(cursor.key, cursor.value);
// Advance the cursor to the next row:
cursor = await cursor.continue();
}
}
getAllItemsFromStoreWithCursor();
تراکنش در این حالت در حالت 'readonly'
باز می شود و روش openCursor
آن فراخوانی می شود. در یک حلقه while
بعدی، ردیف در موقعیت فعلی مکان نما می تواند ویژگی های key
و value
خود را خوانده شود، و شما می توانید به هر روشی که برای برنامه شما منطقی تر است، روی آن مقادیر کار کنید. وقتی آماده شدید، سپس میتوانید متد continue()
شی cursor
را فراخوانی کنید تا به ردیف بعدی بروید، و حلقه while
زمانی که مکاننما به انتهای مجموعه داده میرسد خاتمه مییابد.
از نشانگرها با محدوده و شاخص استفاده کنید
ایندکس ها به شما این امکان را می دهند که داده ها را در یک ذخیره شی با ویژگی دیگری غیر از کلید اصلی واکشی کنید. شما می توانید یک شاخص در هر ویژگی ایجاد کنید، که به keyPath
برای ایندکس تبدیل می شود، محدوده ای را در آن ویژگی مشخص کنید، و داده ها را در محدوده با استفاده از getAll()
یا مکان نما دریافت کنید.
محدوده خود را با استفاده از شی IDBKeyRange
تعریف کنید. و هر یک از روش های زیر:
-
upperBound()
. -
lowerBound()
. -
bound()
(که هر دو است). -
only()
. -
includes()
.
متدهای upperBound()
و lowerBound()
حد بالایی و پایینی محدوده را مشخص می کنند.
IDBKeyRange.lowerBound(indexKey);
یا:
IDBKeyRange.upperBound(indexKey);
هر کدام یک آرگومان می گیرند: مقدار keyPath
شاخص برای موردی که می خواهید به عنوان حد بالا یا پایین مشخص کنید.
bound()
هر دو حد بالا و پایین را مشخص می کند:
IDBKeyRange.bound(lowerIndexKey, upperIndexKey);
محدوده این توابع به طور پیشفرض شامل میشود، به این معنی که شامل دادههایی است که بهعنوان محدودیتهای محدوده مشخص شدهاند. برای کنار گذاشتن این مقادیر، محدوده را به صورت انحصاری با پاس کردن true
به عنوان آرگومان دوم برای lowerBound()
یا upperBound()
یا به عنوان آرگومان های سوم و چهارم bound()
به ترتیب برای حد پایین و بالا مشخص کنید.
مثال بعدی از یک نمایه بر روی ویژگی 'price'
در فروشگاه شی 'foods'
استفاده می کند. فروشگاه در حال حاضر همچنین دارای یک فرم با دو ورودی برای محدوده بالا و پایین محدوده است. از کد زیر برای یافتن غذاهایی با قیمت بین این محدودیت ها استفاده کنید:
import {openDB} from 'idb';
async function searchItems (lower, upper) {
if (!lower === '' && upper === '') {
return;
}
let range;
if (lower !== '' && upper !== '') {
range = IDBKeyRange.bound(lower, upper);
} else if (lower === '') {
range = IDBKeyRange.upperBound(upper);
} else {
range = IDBKeyRange.lowerBound(lower);
}
const db = await openDB('test-db4', 1);
const tx = await db.transaction('foods', 'readonly');
const index = tx.store.index('price');
// Open a cursor on the designated object store:
let cursor = await index.openCursor(range);
if (!cursor) {
return;
}
// Iterate on the cursor, row by row:
while (cursor) {
// Show the data in the row at the current cursor position:
console.log(cursor.key, cursor.value);
// Advance the cursor to the next row:
cursor = await cursor.continue();
}
}
// Get items priced between one and four dollars:
searchItems(1.00, 4.00);
کد مثال ابتدا مقادیر محدودیت ها را دریافت می کند و بررسی می کند که آیا محدودیت ها وجود دارد یا خیر. بلوک بعدی کد تصمیم می گیرد که از کدام روش برای محدود کردن محدوده بر اساس مقادیر استفاده کند. در تعامل پایگاه داده، طبق معمول، فروشگاه شی را در تراکنش باز کنید، سپس شاخص 'price'
را در فروشگاه شی باز کنید. شاخص 'price'
به شما امکان می دهد اقلام را بر اساس قیمت جستجو کنید.
سپس کد یک مکان نما را روی ایندکس باز می کند و در محدوده عبور می کند. مکان نما وعده ای را برمی گرداند که نشان دهنده اولین شی در محدوده است، یا اگر داده ای در محدوده وجود نداشته باشد undefined
. متد cursor.continue()
یک مکان نما نشان دهنده شی بعدی را برمی گرداند و تا زمانی که به انتهای محدوده برسید از طریق حلقه ادامه می دهد.
نسخه سازی پایگاه داده
هنگامی که متد openDB()
فراخوانی می کنید، می توانید شماره نسخه پایگاه داده را در پارامتر دوم مشخص کنید. در تمام مثالهای این راهنما، نسخه روی 1
تنظیم شده است، اما در صورت نیاز به اصلاح یک پایگاه داده میتوان آن را به نسخه جدید ارتقا داد. اگر نسخه مشخصشده بزرگتر از نسخه پایگاه داده موجود باشد، فراخوان upgrade
در شی رویداد اجرا میشود و به شما امکان میدهد ذخیرهها و فهرستهای شی جدید را به پایگاه داده اضافه کنید.
شیء db
در callback upgrade
دارای خاصیت oldVersion
ویژه ای است که نشان دهنده شماره نسخه پایگاه داده ای است که مرورگر به آن دسترسی دارد. میتوانید این شماره نسخه را به یک دستور switch
ارسال کنید تا بلاکهایی از کد را در داخل تماسهای upgrade
بر اساس شماره نسخه پایگاه داده موجود اجرا کنید. در اینجا یک مثال است:
import {openDB} from 'idb';
const db = await openDB('example-database', 2, {
upgrade (db, oldVersion) {
switch (oldVersion) {
case 0:
// Create first object store:
db.createObjectStore('store', { keyPath: 'name' });
case 1:
// Get the original object store, and create an index on it:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('name', 'name');
}
}
});
این مثال جدیدترین نسخه پایگاه داده را روی 2
تنظیم می کند. هنگامی که این کد برای اولین بار اجرا می شود، پایگاه داده هنوز در مرورگر وجود ندارد، بنابراین oldVersion
0
است و دستور switch
در case 0
شروع می شود. در مثال، این یک فروشگاه شیء 'store'
را به پایگاه داده اضافه می کند.
نکته کلیدی: در دستورات switch
، معمولاً بعد از هر بلوک case
یک break
وجود دارد، اما این عمداً در اینجا استفاده نمی شود. به این ترتیب، اگر پایگاه داده موجود چند نسخه عقب باشد، یا اگر وجود نداشته باشد، کد تا زمانی که به روز شود از طریق بقیه بلوک های case
ادامه می یابد. بنابراین در مثال، مرورگر از طریق case 1
به اجرا ادامه میدهد و یک فهرست name
در فروشگاه شیء store
ایجاد میکند.
برای ایجاد یک نمایه 'description'
در فروشگاه شی 'store'
، شماره نسخه را به روز کنید و یک بلوک case
جدید به شرح زیر اضافه کنید:
import {openDB} from 'idb';
const db = await openDB('example-database', 3, {
upgrade (db, oldVersion) {
switch (oldVersion) {
case 0:
// Create first object store:
db.createObjectStore('store', { keyPath: 'name' });
case 1:
// Get the original object store, and create an index on it:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('name', 'name');
case 2:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('description', 'description');
}
}
});
اگر پایگاه داده ای که در مثال قبلی ایجاد کردید هنوز در مرورگر وجود دارد، وقتی این اجرا می شود، oldVersion
2
است. مرورگر case 0
و case 1
را رد می کند و کد case 2
را اجرا می کند که یک نمایه description
ایجاد می کند. پس از آن، مرورگر دارای یک پایگاه داده در نسخه 3 است که حاوی یک فروشگاه شی store
با فهرست های name
و description
است.
در ادامه مطلب
منابع زیر اطلاعات و زمینه های بیشتری را برای استفاده از IndexedDB ارائه می دهند.
اسناد IndexedDB
- مخزن
idb
Github - با استفاده از IndexedDB
- مفاهیم اساسی در پشت IndexedDB
- مشخصات API 3.0 پایگاه داده نمایه شده