برای ایجاد یک تجربه آفلاین جامد، PWA شما نیاز به مدیریت ذخیره سازی دارد. در بخش حافظه پنهان یاد گرفتید که ذخیره سازی کش یکی از گزینه های ذخیره داده ها در دستگاه است. در این فصل، نحوه مدیریت داده های آفلاین، از جمله ماندگاری داده ها، محدودیت ها و ابزارهای موجود را به شما نشان خواهیم داد.
ذخیره سازی
ذخیره سازی فقط مربوط به فایل ها و دارایی ها نیست، بلکه می تواند انواع دیگری از داده ها را نیز شامل شود. در تمام مرورگرهایی که از PWA پشتیبانی می کنند، API های زیر برای ذخیره سازی روی دستگاه در دسترس هستند:
- IndexedDB : یک گزینه ذخیره سازی شی NoSQL برای داده های ساخت یافته و حباب ها (داده های باینری).
- WebStorage: راهی برای ذخیرهسازی جفتهای رشته کلید/مقدار، با استفاده از حافظه محلی یا ذخیرهسازی جلسه. در زمینه کارمند خدماتی در دسترس نیست. این API همزمان است، بنابراین برای ذخیره سازی داده های پیچیده توصیه نمی شود.
- حافظه پنهان: همانطور که در ماژول Caching پوشش داده شده است.
میتوانید تمام فضای ذخیرهسازی دستگاه را با Storage Manager API در پلتفرمهای پشتیبانیشده مدیریت کنید. Cache Storage API و IndexedDB دسترسی ناهمزمان به ذخیره سازی دائمی را برای PWA ها فراهم می کند و می توان از رشته اصلی، وب کارگران و سرویس دهندگان به آن دسترسی داشت. هر دو نقش اساسی در کارکرد قابل اعتماد PWA ها در زمانی که شبکه پوسته پوسته است یا وجود ندارد، ایفا می کنند. اما چه زمانی باید از هر کدام استفاده کرد؟
از Cache Storage API برای منابع شبکه استفاده کنید، چیزهایی که با درخواست از طریق URL به آنها دسترسی خواهید داشت، مانند HTML، CSS، جاوا اسکریپت، تصاویر، ویدیوها و صدا.
از IndexedDB برای ذخیره داده های ساخت یافته استفاده کنید. این شامل دادههایی است که باید به شیوهای NoSQL مانند قابل جستجو یا ترکیب باشند، یا دادههای دیگری مانند دادههای خاص کاربر که لزوماً با درخواست URL مطابقت ندارند. توجه داشته باشید که IndexedDB برای جستجوی متن کامل طراحی نشده است.
IndexedDB
برای استفاده از IndexedDB ، ابتدا یک پایگاه داده را باز کنید. اگر پایگاه داده ای وجود نداشته باشد، این یک پایگاه داده جدید ایجاد می کند. IndexedDB یک API ناهمزمان است، اما به جای برگرداندن یک Promise، یک تماس پاسخ می گیرد. مثال زیر از کتابخانه idb جیک آرچیبالد استفاده میکند که یک بسته کوچک Promise برای IndexedDB است. کتابخانه های کمکی برای استفاده از IndexedDB لازم نیستند، اما اگر می خواهید از دستور Promise استفاده کنید، کتابخانه idb
یک گزینه است.
مثال زیر یک پایگاه داده برای نگهداری دستورهای آشپزی ایجاد می کند.
ایجاد و باز کردن پایگاه داده
برای باز کردن پایگاه داده:
- از تابع
openDB
برای ایجاد یک پایگاه داده IndexedDB جدید به نامcookbook
استفاده کنید. از آنجایی که پایگاه های داده IndexedDB نسخه بندی شده اند، باید هر زمان که در ساختار پایگاه داده تغییراتی ایجاد می کنید، شماره نسخه را افزایش دهید. پارامتر دوم نسخه پایگاه داده است. در مثال 1 تنظیم شده است. - یک شی مقداردهی اولیه حاوی یک callback
upgrade()
بهopenDB()
ارسال می شود. تابع callback زمانی فراخوانی می شود که پایگاه داده برای اولین بار نصب می شود یا زمانی که به نسخه جدید ارتقا می یابد. این تابع تنها مکانی است که در آن اعمال می توانند اتفاق بیفتند. اقدامات ممکن است شامل ایجاد فروشگاههای شی جدید (ساختارهایی که IndexedDB برای سازماندهی دادهها استفاده میکند)، یا فهرستهایی (که میخواهید در آنها جستجو کنید) باشد. این جایی است که انتقال داده نیز باید اتفاق بیفتد. به طور معمول، تابعupgrade()
حاوی یک دستورswitch
بدون دستوراتbreak
است تا اجازه دهد هر مرحله بر اساس نسخه قدیمی پایگاه داده به ترتیب انجام شود.
import { openDB } from 'idb';
async function createDB() {
// Using https://github.com/jakearchibald/idb
const db = await openDB('cookbook', 1, {
upgrade(db, oldVersion, newVersion, transaction) {
// Switch over the oldVersion, *without breaks*, to allow the database to be incrementally upgraded.
switch(oldVersion) {
case 0:
// Placeholder to execute when database is created (oldVersion is 0)
case 1:
// Create a store of objects
const store = db.createObjectStore('recipes', {
// The `id` property of the object will be the key, and be incremented automatically
autoIncrement: true,
keyPath: 'id'
});
// Create an index called `name` based on the `type` property of objects in the store
store.createIndex('type', 'type');
}
}
});
}
مثال، یک شی ذخیرهسازی در پایگاهداده cookbook
به نام recipes
ایجاد میکند، با ویژگی id
به عنوان کلید فهرست فروشگاه تنظیم میشود و شاخص دیگری به نام type
را بر اساس ویژگی type
ایجاد میکند.
بیایید نگاهی به فروشگاه شی که به تازگی ایجاد شده است بیندازیم. پس از افزودن دستور العمل ها به فروشگاه شی و باز کردن DevTools در مرورگرهای مبتنی بر Chromium یا Web Inspector در Safari، این چیزی است که باید انتظار داشته باشید که ببینید:
افزودن داده ها
IndexedDB از تراکنش ها استفاده می کند. تراکنش ها اقدامات را با هم گروه بندی می کنند، بنابراین آنها به عنوان یک واحد رخ می دهند. آنها کمک می کنند تا اطمینان حاصل شود که پایگاه داده همیشه در یک وضعیت سازگار است. اگر چندین نسخه از برنامه خود را در حال اجرا دارید، برای جلوگیری از نوشتن همزمان روی یک داده نیز بسیار مهم هستند. برای افزودن داده ها:
- یک تراکنش را با
mode
تنظیم شده برایreadwrite
شروع کنید. - ذخیره شی را دریافت کنید، جایی که داده ها را اضافه می کنید.
- با داده هایی که ذخیره می کنید
add()
فراخوانی کنید. این روش داده ها را به شکل فرهنگ لغت (به عنوان جفت کلید/مقدار) دریافت می کند و آن را به ذخیره شی اضافه می کند. فرهنگ لغت باید با استفاده از شبیه سازی ساختاریافته قابل شبیه سازی باشد. اگر می خواهید یک شی موجود را به روز کنید، به جای آن متدput()
فراخوانی می کنید.
تراکنش ها دارای یک وعده done
هستند که وقتی تراکنش با موفقیت انجام شد یا با خطای تراکنش رد می شود برطرف می شود.
همانطور که مستندات کتابخانه IDB توضیح می دهد، اگر در حال نوشتن در پایگاه داده هستید، tx.done
سیگنالی است که همه چیز با موفقیت به پایگاه داده متعهد شده است. با این حال، سودمند است که منتظر عملیات فردی باشید تا بتوانید خطاهایی را که باعث شکست تراکنش می شود، مشاهده کنید.
// Using https://github.com/jakearchibald/idb
async function addData() {
const cookies = {
name: "Chocolate chips cookies",
type: "dessert",
cook_time_minutes: 25
};
const tx = await db.transaction('recipes', 'readwrite');
const store = tx.objectStore('recipes');
store.add(cookies);
await tx.done;
}
هنگامی که کوکی ها را اضافه کردید، دستور غذا همراه با دستور العمل های دیگر در پایگاه داده قرار می گیرد. شناسه به طور خودکار توسط indexedDB تنظیم و افزایش می یابد. اگر این کد را دو بار اجرا کنید، دو ورودی کوکی یکسان خواهید داشت.
بازیابی داده ها
در اینجا نحوه دریافت داده از IndexedDB آمده است:
- یک تراکنش را شروع کنید و شی ذخیره یا ذخایر و در صورت تمایل نوع تراکنش را مشخص کنید.
- از آن تراکنش
objectStore()
را فراخوانی کنید. مطمئن شوید که نام فروشگاه شی را مشخص کرده اید. - با کلیدی که می خواهید دریافت کنید
get()
فراخوانی کنید. به طور پیش فرض فروشگاه از کلید خود به عنوان شاخص استفاده می کند.
// Using https://github.com/jakearchibald/idb
async function getData() {
const tx = await db.transaction('recipes', 'readonly')
const store = tx.objectStore('recipes');
// Because in our case the `id` is the key, we would
// have to know in advance the value of the id to
// retrieve the record
const value = await store.get([id]);
}
مدیر ذخیره سازی
دانستن نحوه مدیریت فضای ذخیره سازی PWA برای ذخیره و پخش صحیح پاسخ های شبکه بسیار مهم است.
ظرفیت ذخیره سازی بین تمام گزینه های ذخیره سازی، از جمله حافظه پنهان، IndexedDB، ذخیره سازی وب، و حتی فایل Service Worker و وابستگی های آن مشترک است. با این حال، میزان فضای ذخیره سازی موجود در مرورگرهای مختلف متفاوت است. شما به احتمال زیاد تمام نمی شود. سایت ها می توانند مگابایت و حتی گیگابایت داده را در برخی از مرورگرها ذخیره کنند. برای مثال، کروم به مرورگر اجازه میدهد تا 80 درصد از کل فضای دیسک را استفاده کند و یک منبع فردی میتواند تا 60 درصد از کل فضای دیسک را استفاده کند. برای مرورگرهایی که از Storage API پشتیبانی میکنند، میتوانید بدانید هنوز چقدر فضای ذخیرهسازی برای برنامه شما، سهمیه آن و استفاده از آن در دسترس است. مثال زیر از Storage API برای بدست آوردن سهمیه و میزان مصرف تخمینی استفاده می کند، سپس درصد استفاده شده و بایت های باقیمانده را محاسبه می کند. توجه داشته باشید که navigator.storage
نمونه ای از StorageManager
را برمی گرداند. یک رابط Storage
جداگانه وجود دارد و به راحتی می توان آنها را گیج کرد.
if (navigator.storage && navigator.storage.estimate) {
const quota = await navigator.storage.estimate();
// quota.usage -> Number of bytes used.
// quota.quota -> Maximum number of bytes available.
const percentageUsed = (quota.usage / quota.quota) * 100;
console.log(`You've used ${percentageUsed}% of the available storage.`);
const remaining = quota.quota - quota.usage;
console.log(`You can write up to ${remaining} more bytes.`);
}
در Chromium DevTools، با باز کردن بخش Storage در برگه Application ، میتوانید سهمیه سایت خود و میزان فضای ذخیرهسازی مصرفشده را بر اساس مواردی که از آن استفاده میکنند، مشاهده کنید.
فایرفاکس و سافاری صفحه نمایش خلاصه ای برای مشاهده تمام سهمیه ذخیره سازی و استفاده برای مبدا فعلی ارائه نمی دهند.
ماندگاری داده ها
میتوانید از مرورگر برای ذخیرهسازی دائمی روی پلتفرمهای سازگار بخواهید تا از خروج خودکار دادهها پس از عدم فعالیت یا فشار ذخیرهسازی جلوگیری کنید. اگر اجازه داده شود، مرورگر هرگز داده ها را از حافظه خارج نمی کند. این حفاظت شامل ثبت نام کارگر سرویس، پایگاه داده IndexedDB و فایلهای موجود در حافظه پنهان است. توجه داشته باشید که کاربران همیشه مسئول هستند و میتوانند در هر زمانی فضای ذخیرهسازی را حذف کنند، حتی اگر مرورگر فضای ذخیرهسازی دائمی را اعطا کرده باشد.
برای درخواست ذخیره سازی دائمی، با StorageManager.persist()
تماس بگیرید. مانند قبل، رابط StorageManager
از طریق ویژگی navigator.storage
دسترسی دارد.
async function persistData() {
if (navigator.storage && navigator.storage.persist) {
const result = await navigator.storage.persist();
console.log(`Data persisted: ${result}`);
}
همچنین میتوانید با فراخوانی StorageManager.persisted()
بررسی کنید که آیا فضای ذخیرهسازی دائمی قبلاً در مبدا فعلی اعطا شده است. فایرفاکس برای استفاده از فضای ذخیره سازی دائمی از کاربر اجازه درخواست می کند. مرورگرهای مبتنی بر Chromium برای تعیین اهمیت محتوا برای کاربر، بر اساس یک اکتشافی تداوم میدهند یا آن را رد میکنند. یکی از معیارهای Google Chrome، برای مثال، نصب PWA است. اگر کاربر نمادی برای PWA در سیستم عامل نصب کرده باشد، مرورگر ممکن است به ذخیره سازی دائمی اعطا کند.