گزینههای مختلفی برای ذخیره دادهها در مرورگر وجود دارد. کدام یک برای نیازهای شما بهترین است؟
اتصال به اینترنت میتواند در حین حرکت ناپایدار یا اصلاً وجود نداشته باشد، به همین دلیل پشتیبانی آفلاین و عملکرد قابل اعتماد از ویژگیهای رایج در برنامههای وب پیشرونده هستند. حتی در محیطهای بیسیم بینقص، استفادهی هوشمندانه از ذخیرهسازی و سایر تکنیکهای ذخیرهسازی میتواند تجربهی کاربری را به طور قابل توجهی بهبود بخشد. روشهای مختلفی برای ذخیرهسازی منابع استاتیک برنامه (HTML، جاوا اسکریپت، CSS، تصاویر و غیره) و دادهها (دادههای کاربر، مقالات خبری و غیره) وجود دارد. اما بهترین راه حل کدام است؟ چقدر میتوانید ذخیره کنید؟ چگونه از حذف آن جلوگیری میکنید؟
از چی باید استفاده کنم؟
در اینجا یک توصیه کلی برای ذخیره منابع ارائه شده است:
- برای منابع شبکهای لازم برای بارگذاری برنامهتان، از API ذخیرهسازی کش (بخشی از service workerها ) استفاده کنید.
- برای محتوای مبتنی بر فایل، از سیستم فایل خصوصی Origin (OPFS) استفاده کنید.
- برای دادههای دیگر، از IndexedDB (با یک پوشش promise ) استفاده کنید.
IndexedDB، OPFS و Cache Storage API در هر مرورگر مدرنی پشتیبانی میشوند. آنها ناهمگام هستند و نخ اصلی را مسدود نمیکنند (اما یک نوع همگام از OPFS نیز وجود دارد که منحصراً در web workerها موجود است). آنها از طریق شیء window ، web workerها و service workerها قابل دسترسی هستند و این امکان را فراهم میکند که از آنها در هر کجای کد خود استفاده کنید.
در مورد سایر مکانیسمهای ذخیرهسازی چطور؟
چندین مکانیزم ذخیرهسازی دیگر در مرورگر موجود است، اما کاربرد محدودی دارند و ممکن است مشکلات عملکردی قابل توجهی ایجاد کنند.
SessionStorage مختص تب است و محدودهی آن به طول عمر تب بستگی دارد. ممکن است برای ذخیرهی مقادیر کمی از اطلاعات مختص به جلسه، مثلاً یک کلید IndexedDB، مفید باشد. باید با احتیاط از آن استفاده کرد زیرا همگام است و رشتهی اصلی را مسدود میکند. حجم آن به حدود ۵ مگابایت محدود میشود و فقط میتواند شامل رشتهها باشد. از آنجایی که مختص تب است، از طریق web workerها یا service workerها قابل دسترسی نیست.
باید از LocalStorage اجتناب کرد زیرا همگام است و رشته اصلی را مسدود میکند. حجم آن به حدود ۵ مگابایت محدود شده است و فقط میتواند شامل رشتهها باشد. LocalStorage از طریق web workerها یا service workerها قابل دسترسی نیست.
کوکیها کاربردهای خود را دارند، اما نباید برای ذخیرهسازی استفاده شوند. کوکیها با هر درخواست HTTP ارسال میشوند، بنابراین ذخیره هر چیزی بیش از مقدار کمی داده، اندازه هر درخواست وب را به میزان قابل توجهی افزایش میدهد. آنها همگام هستند و از طریق web workerها قابل دسترسی نیستند. مانند LocalStorage و SessionStorage، کوکیها فقط به رشتهها محدود میشوند.
رابط برنامهنویسی کاربردی دسترسی به سیستم فایل (File System Access API) به گونهای طراحی شده است که کاربران بتوانند فایلها را در سیستم فایل محلی خود بخوانند و ویرایش کنند. کاربر باید قبل از اینکه یک صفحه بتواند هر فایل محلی را بخواند یا بنویسد، مجوز را اعطا کند و مجوزها در طول جلسات (session) حفظ نمیشوند، مگر اینکه یک فایل در IndexedDB ذخیره شده باشد. رابط برنامهنویسی کاربردی دسترسی به سیستم فایل (File System Access API) برای مواردی مانند ویرایشگرها مناسب است، جایی که باید یک فایل را باز کنید، آن را تغییر دهید و سپس احتمالاً تغییرات را در فایل ذخیره کنید.
API سیستم فایل و API فایلرایتر، متدهایی برای خواندن و نوشتن فایلها در یک سیستم فایل سندباکسشده ارائه میدهند. اگرچه این API ناهمزمان است، اما توصیه نمیشود زیرا فقط در مرورگرهای مبتنی بر کرومیوم در دسترس است.
چقدر میتوانم ذخیره کنم؟
به طور خلاصه، خیلی زیاد ، حداقل چند صد مگابایت، و به طور بالقوه صدها گیگابایت یا بیشتر. پیادهسازی مرورگرها متفاوت است، اما میزان فضای ذخیرهسازی موجود معمولاً بر اساس میزان فضای ذخیرهسازی موجود در دستگاه است.
- کروم به مرورگر اجازه میدهد تا ۸۰٪ از کل فضای دیسک را استفاده کند. یک منبع میتواند تا ۶۰٪ از کل فضای دیسک را استفاده کند. میتوانید از API StorageManager برای تعیین حداکثر سهمیه موجود استفاده کنید. سایر مرورگرهای مبتنی بر کرومیوم ممکن است متفاوت باشند.
- در حالت ناشناس، کروم میزان فضای ذخیرهسازی قابل استفاده توسط هر کاربر را تقریباً به ۵٪ از کل فضای دیسک کاهش میدهد.
- اگر کاربر گزینه «پاک کردن کوکیها و دادههای سایت هنگام بستن همه پنجرهها» را در کروم فعال کرده باشد، سهمیه ذخیرهسازی به طور قابل توجهی کاهش مییابد و حداکثر تقریباً به ۳۰۰ مگابایت میرسد.
- فایرفاکس به مرورگر اجازه میدهد تا ۵۰٪ از فضای خالی دیسک را استفاده کند. یک گروه eTLD+1 (مثلاً
example.com،www.example.comوfoo.bar.example.com) ممکن است تا ۲ گیگابایت فضا اشغال کند . میتوانید از StorageManager API برای تعیین میزان فضای باقیمانده استفاده کنید. - به نظر میرسد سافاری (هم دسکتاپ و هم موبایل) حدود ۱ گیگابایت فضا را مجاز میداند. وقتی به این حد برسد، سافاری به کاربر اطلاع میدهد و این محدودیت را به صورت پلکانی ۲۰۰ مگابایتی افزایش میدهد. من نتوانستم هیچ سند رسمی در این مورد پیدا کنم.
- اگر یک PWA به صفحه اصلی سافاری موبایل اضافه شود، یک محفظه ذخیرهسازی جدید ایجاد میکند و هیچ چیز بین PWA و سافاری موبایل به اشتراک گذاشته نمیشود. هنگامی که سهمیه برای یک PWA نصب شده پر شود، به نظر نمیرسد راهی برای درخواست فضای ذخیرهسازی اضافی وجود داشته باشد.
در گذشته، اگر سایتی از آستانهی مشخصی از دادههای ذخیرهشده تجاوز میکرد، مرورگر از کاربر میخواست که اجازهی استفاده از دادههای بیشتر را بدهد. برای مثال، اگر مبدأ بیش از ۵۰ مگابایت استفاده میکرد، مرورگر از کاربر میخواست که اجازه دهد تا ۱۰۰ مگابایت ذخیره کند، سپس با افزایش ۵۰ مگابایتی دوباره این درخواست را مطرح میکرد.
امروزه، اکثر مرورگرهای مدرن به کاربر اطلاع نمیدهند و به سایت اجازه میدهند تا حداکثر سهمیه اختصاص داده شده خود را استفاده کند. به نظر میرسد مرورگر سافاری استثنا باشد، که وقتی سهمیه ذخیرهسازی از حد مجاز تجاوز میکند، درخواست مجوز برای افزایش سهمیه اختصاص داده شده را میدهد. اگر یک مبدا سعی کند بیش از سهمیه اختصاص داده شده خود استفاده کند، تلاشهای بیشتر برای نوشتن دادهها با شکست مواجه خواهد شد.
چگونه میتوانم بررسی کنم که چقدر فضای ذخیرهسازی در دسترس است؟
در بسیاری از مرورگرها ، میتوانید از API مربوط به StorageManager برای تعیین میزان فضای ذخیرهسازی موجود برای مبدا و میزان فضای اشغال شده توسط آن استفاده کنید. این API تعداد کل بایتهای استفاده شده توسط IndexedDB و Cache API را گزارش میدهد و امکان محاسبه تقریبی فضای ذخیرهسازی باقیمانده را فراهم میکند.
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.`);
}
شما باید خطاهای مربوط به سهمیه بیش از حد را بررسی کنید (به پایین مراجعه کنید). در برخی موارد، ممکن است سهمیه موجود از مقدار واقعی فضای ذخیرهسازی موجود بیشتر شود.
بازرسی
در طول توسعه، میتوانید از DevTools مرورگر خود برای بررسی انواع مختلف فضای ذخیرهسازی و پاک کردن تمام دادههای ذخیره شده استفاده کنید.
یک ویژگی جدید در کروم ۸۸ اضافه شده است که به شما امکان میدهد سهمیه ذخیرهسازی سایت را در پنل ذخیرهسازی لغو کنید. این ویژگی به شما امکان میدهد دستگاههای مختلف را شبیهسازی کرده و رفتار برنامههای خود را در سناریوهای کمبود دیسک آزمایش کنید. به برنامه و سپس ذخیرهسازی بروید، کادر انتخاب شبیهسازی سهمیه ذخیرهسازی سفارشی را فعال کنید و هر عدد معتبری را برای شبیهسازی سهمیه ذخیرهسازی وارد کنید.
هنگام کار روی این راهنما، من یک ابزار ساده نوشتم تا سعی کنم به سرعت از حداکثر فضای ذخیرهسازی ممکن استفاده کنم. این یک راه سریع برای آزمایش مکانیسمهای مختلف ذخیرهسازی است و اینکه ببینید وقتی از تمام سهمیه خود استفاده میکنید چه اتفاقی میافتد.
چگونه میتوان با عبور از سهمیه تعیینشده برخورد کرد؟
وقتی از سهمیه تعیینشده فراتر میروید چه باید بکنید؟ از همه مهمتر، همیشه باید خطاهای نوشتن را شناسایی و مدیریت کنید، چه QuotaExceededError باشد و چه چیز دیگری. سپس، بسته به طراحی برنامهتان، تصمیم بگیرید که چگونه آن را مدیریت کنید. به عنوان مثال، محتوایی را که مدت زیادی است به آن دسترسی نداشتهاید حذف کنید، دادهها را بر اساس اندازه حذف کنید، یا راهی برای کاربران فراهم کنید تا آنچه را که میخواهند حذف کنند، انتخاب کنند.
هر دوی IndexedDB و Cache API وقتی از سهمیه موجود فراتر بروید، یک DOMError با نام QuotaExceededError ایجاد میکنند.
IndexedDB
اگر مبدا از سهمیه خود فراتر رفته باشد، تلاش برای نوشتن در IndexedDB با شکست مواجه خواهد شد. تابع onabort() مربوط به تراکنش فراخوانی شده و یک رویداد را ارسال میکند. این رویداد شامل یک DOMException در ویژگی error خواهد بود. بررسی name خطا QuotaExceededError برمیگرداند.
const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
const error = event.target.error; // DOMException
if (error.name == 'QuotaExceededError') {
// Fallback code goes here
}
};
رابط برنامهنویسی کاربردی حافظه پنهان
اگر مبدا از سهمیه خود فراتر رفته باشد، تلاش برای نوشتن در Cache API با QuotaExceededError DOMException رد خواهد شد.
try {
const cache = await caches.open('my-cache');
await cache.add(new Request('/sample1.jpg'));
} catch (err) {
if (error.name === 'QuotaExceededError') {
// Fallback code goes here
}
}
اخراج چگونه کار میکند؟
ذخیرهسازی وب به دو دسته «بهترین تلاش» و «پایدار» طبقهبندی میشود. بهترین تلاش به این معنی است که فضای ذخیرهسازی میتواند توسط مرورگر بدون ایجاد وقفه در کار کاربر پاک شود، اما برای دادههای بلندمدت یا حیاتی دوام کمتری دارد. فضای ذخیرهسازی پایدار وقتی فضای ذخیرهسازی کم باشد، بهطور خودکار پاک نمیشود. کاربر باید بهصورت دستی (از طریق تنظیمات مرورگر) این فضای ذخیرهسازی را پاک کند.
به طور پیشفرض، دادههای یک سایت (از جمله IndexedDB، Cache API و غیره) در دسته بهترین تلاش قرار میگیرند، به این معنی که مگر اینکه سایتی درخواست ذخیرهسازی مداوم کرده باشد، مرورگر ممکن است دادههای سایت را به صلاحدید خود حذف کند، به عنوان مثال، زمانی که فضای ذخیرهسازی دستگاه کم است.
سیاست اخراج با بهترین تلاش عبارت است از:
- مرورگرهای مبتنی بر کرومیوم وقتی فضای مرورگر تمام شود، شروع به حذف دادهها میکنند و ابتدا تمام دادههای سایت را از آخرین منبع استفادهشده پاک میکنند، سپس منبع بعدی، تا زمانی که مرورگر دیگر از حد مجاز عبور نکند.
- فایرفاکس وقتی فضای دیسک موجود پر شود، شروع به حذف دادهها میکند و ابتدا تمام دادههای سایت را از آخرین منبع استفادهشده پاک میکند، سپس منبع بعدی، تا زمانی که مرورگر دیگر از حد مجاز تجاوز نکند.
- سافاری قبلاً دادهها را حذف نمیکرد، اما اخیراً محدودیت هفت روزه جدیدی را برای تمام حافظههای قابل نوشتن اعمال کرده است (به پایین مراجعه کنید).
از iOS و iPadOS 13.4 و Safari 13.1 در macOS، یک محدودیت هفت روزه برای تمام فضای ذخیرهسازی قابل نوشتن اسکریپت، از جمله IndexedDB، ثبت نام service worker و Cache API وجود دارد. این بدان معناست که Safari پس از هفت روز استفاده از Safari، در صورت عدم تعامل کاربر با سایت، تمام محتوا را از حافظه پنهان خارج میکند. این سیاست حذف شامل PWA های نصب شده که به صفحه اصلی اضافه شدهاند، نمیشود . برای جزئیات کامل، به بخش «مسدود کردن کامل کوکیهای شخص ثالث و موارد دیگر» در وبلاگ WebKit مراجعه کنید.
سطلهای ذخیرهسازی
ایده اصلی API مربوط به Storage Buckets ، اعطای قابلیت ایجاد چندین Storage Buckets به سایتها است، که در آن مرورگر میتواند هر Bucket را مستقل از Bucketهای دیگر حذف کند. این امر به توسعهدهندگان اجازه میدهد تا اولویتبندی حذف را مشخص کنند تا مطمئن شوند که ارزشمندترین دادهها حذف نمیشوند.
نکتهی اضافی: چرا از یک wrapper برای IndexedDB استفاده کنیم؟
IndexedDB یک API سطح پایین است که قبل از استفاده نیاز به تنظیمات قابل توجهی دارد، که میتواند به ویژه برای ذخیره دادههای با پیچیدگی کم، دردسرساز باشد. برخلاف اکثر APIهای مدرن مبتنی بر promise، این API مبتنی بر رویداد است. پوششدهندههای promise مانند idb برای IndexedDB برخی از ویژگیهای قدرتمند را پنهان میکنند، اما مهمتر از آن، سازوکار پیچیده (مانند تراکنشها، نسخهبندی طرحواره) که با کتابخانه IndexedDB همراه است را پنهان میکنند.
جایزه: SQLite Wasm
پس از اینکه Web SQL منسوخ و از کروم حذف شد، گوگل با توسعهدهندگان پایگاه داده محبوب SQLite همکاری کرد تا جایگزینی برای Web SQL مبتنی بر SQLite ارائه دهد. برای جزئیات بیشتر در مورد نحوه استفاده از SQLite Wasm در مرورگر که توسط Origin Private File System پشتیبانی میشود، آن را مطالعه کنید.
نتیجهگیری
روزهای فضای ذخیرهسازی محدود و وادار کردن کاربر به ذخیره دادههای بیشتر و بیشتر گذشته است. سایتها میتوانند به طور مؤثر تمام منابع و دادههایی را که برای اجرا نیاز دارند، ذخیره کنند. با استفاده از API StorageManager میتوانید تعیین کنید که چه مقدار در دسترس شماست و چه مقدار از آن را استفاده کردهاید. و با فضای ذخیرهسازی پایدار ، مگر اینکه کاربر آن را حذف کند، میتوانید از آن در برابر حذف محافظت کنید.
منابع اضافی
ممنون
تشکر ویژه از جارید گودمن، فیل والتون، ایجی کیتامورا، دنیل مورفی، داروین هوانگ، جاش بل، ماریجن کرویسلبرینک و ویکتور کاستان برای بررسی این راهنما. با تشکر از ایجی کیتامورا، ادی عثمانی و مارک کوهن که مقالات اصلی که این راهنما بر اساس آنها نوشته شده است را نوشتند. ایجی ابزاری مفید به نام Browser Storage Abuser نوشت که در اعتبارسنجی رفتار فعلی مفید بود. این ابزار به شما امکان میدهد تا حد امکان داده ذخیره کنید و محدودیتهای ذخیرهسازی را در مرورگر خود مشاهده کنید. با تشکر از فرانسوا بوفورت که برای کشف محدودیتهای ذخیرهسازی سافاری، آن را بررسی کرد و از توماس اشتاینر برای افزودن اطلاعاتی در مورد سیستم فایل خصوصی مبدا، سطلهای ذخیرهسازی، SQLite Wasm و بهروزرسانی کلی محتوا در سال ۲۰۲۴.