هناك العديد من الخيارات المختلفة لتخزين البيانات في المتصفّح. أيهما يناسب احتياجاتك؟
قد تكون اتصالات الإنترنت غير متّصلة أو غير متوفّرة أثناء التنقل، لذلك، إنّ إتاحة الاستخدام بلا إنترنت والأداء الموثوق به هما سمتان شائعتان في تطبيقات الويب التقدّمية. حتى في البيئات اللاسلكية المثالية، يمكن أن يؤدي الاستخدام الحكيم لميزة التخزين المؤقت وتقنيات التخزين الأخرى إلى تحسين تجربة المستخدم بشكل كبير. هناك عدة طرق للاحتفاظ بنسخة مؤقتة من موارد تطبيقك الثابتة (HTML وJavaScript وCSS والصور وما إلى ذلك) و بياناته (بيانات المستخدمين والمقالات الإخبارية وما إلى ذلك). ولكن ما الحل الأفضل؟ كم يمكنك تخزينه؟ كيف يمكنك منع إزالة المحتوى؟
ماذا عليّ أن أستخدم؟
في ما يلي اقتراح عام لتخزين الموارد:
- بالنسبة إلى موارد الشبكة اللازمة لتحميل تطبيقك، استخدِم Cache Storage API (جزء من عاملي الخدمات).
- بالنسبة إلى المحتوى المستنِد إلى الملفات، استخدِم نظام الملفات الخاص بمنشأ المحتوى (OPFS).
- بالنسبة إلى البيانات الأخرى، استخدِم IndexedDB (مع حزمة promises).
تتوفّر واجهتا برمجة التطبيقات IndexedDB وOPFS وCache Storage API في كل متصفّح حديث.
وهي غير متزامنة، ولن تحظر سلسلة التعليمات الرئيسية (ولكن هناك أيضًا نوع متزامن
من OPFS متاح حصريًا في مهام الويب). ويمكن الوصول إليها من خلال الكائن window
وعاملي الويب وعاملي الخدمات، ما يتيح إمكانية استخدامها في أي مكان في الرمز البرمجي.
ماذا عن آليات التخزين الأخرى؟
تتوفّر عدة آليات تخزين أخرى في المتصفّح، ولكن استخدامها محدود وقد تتسبب في مشاكل كبيرة في الأداء.
SessionStorage خاص بعلامة التبويب، ويكون نطاقه محدودًا بفترة استخدام علامة التبويب. وقد يكون مفيدًا لتخزين كميات صغيرة من المعلومات المتعلّقة بالجلسة، مثلاً مفتاح IndexedDB. ويجب استخدامه بحذر لأنه متزامن وسيحظر سلسلة التعليمات الرئيسية. وهو لا يزيد حجمه عن 5 ميغابايت ويمكن أن يحتوي على سلاسل فقط. ولأنّه خاص بعلامة التبويب، لا يمكن الوصول إليه من مشغّلات الويب أو مشغّلات الخدمات.
يجب تجنُّب استخدام LocalStorage، لأنّها متزامنة وستحظر سلسلة التعليمات الرئيسية. لا يمكن أن تتجاوز 5 ميغابايت ويمكن أن تحتوي على سلاسل فقط. لا يمكن الوصول إلى LocalStorage من خلال مشغّلات الويب أو مشغّلات الخدمات.
لملفات تعريف الارتباط استخداماتها، ولكن يجب عدم استخدامها للتخزين. يتم إرسال ملفات تعريف الارتباط مع كل طلب HTTP، لذا سيؤدي تخزين أي شيء أكثر من كمية صغيرة من البيانات إلى زيادة حجم كل طلب ويب بشكل كبير. فهي متزامنة ولا يمكن الوصول إليها من قِبل العاملين على الويب. مثل LocalStorage وsessionStorage، تقتصر ملفات تعريف الارتباط على السلاسل فقط.
تم تصميم File System Access API لتمكين المستخدمين من قراءة الملفات وتعديلها على نظام الملفات على الجهاز. على المستخدم منح الإذن قبل أن تتمكّن الصفحة من قراءة أي ملف على الجهاز أو الكتابة فيه، ولا يتم الاحتفاظ بالأذونات على مستوى الجلسات، ما لم يتم تخزين معرّف ملف في IndexedDB. إنّ واجهة برمجة التطبيقات File System Access API هي الأنسب لحالات الاستخدام، مثل التطبيقات المزوّدة بأدوات تعديل، حيث تحتاج إلى فتح ملف وتعديله، ثم حفظ التغييرات في الملف.
توفّر واجهتا File System API وFileWriter API طُرقًا لقراءة الملفات وكتابتها في نظام ملفات محصور. على الرغم من أنّه غير متزامن، لا يُنصح به لأنّه لا يتوفّر إلّا في المتصفّحات المستنِدة إلى Chromium.
ما هو حجم المحتوى الذي يمكنني تخزينه؟
باختصار، الكثير، أي ما لا يقل عن بضع مئات من الميغابايت، وقد يصل إلى مئات الغيغابايت أو أكثر. تختلف عمليات تنفيذ المتصفّحات، ولكن يستند مقدار مساحة التخزين المتاحة عادةً إلى مقدار مساحة التخزين المتاحة على الجهاز.
- يتيح Chrome للمتصفّح استخدام ما يصل إلى% 80 من إجمالي مساحة القرص. يمكن أن تستهلك نقطة المصدر
ما يصل إلى %60 من إجمالي مساحة القرص. يمكنك استخدام واجهة برمجة التطبيقات StorageManager
API لتحديد الحد الأقصى للحصة المتاحة. قد تختلف المتصفحات الأخرى المستندة إلى Chromium.
- في "وضع التصفّح المتخفي"، يقلّل Chrome مقدار مساحة التخزين التي يمكن لمصدر استخدامها إلى %5 تقريبًا من إجمالي مساحة القرص.
- إذا فعَّل المستخدم خيار "محو ملفات تعريف الارتباط وبيانات المواقع الإلكترونية عند إغلاق جميع النوافذ" في Chrome، يتم تقليل حصة مساحة التخزين بشكل كبير إلى 300 ميغابايت كحد أقصى تقريبًا.
- يتيح Firefox للمتصفح استخدام ما يصل إلى 50% من مساحة القرص الخالية. مجموعة
eTLD+1
(مثل
example.com
وwww.example.com
وfoo.bar.example.com
) قد يستخدم ما يصل إلى 2 غيغابايت. يمكنك استخدام واجهة برمجة التطبيقات StorageManager API لتحديد المساحة التي لا تزال متاحة. - يبدو أنّ متصفّح Safari (على أجهزة الكمبيوتر المكتبي والأجهزة الجوّالة) يسمح بتحميل 1 غيغابايت تقريبًا. عند بلوغ الحد، سيطلب Safari من المستخدم زيادة الحد إلى 200 ميغابايت. لم أتمكّن من العثور على أي مستندات رسمية حول هذا الموضوع.
- في حال إضافة تطبيق متوافق مع تقنية الويب التقدّمي إلى الشاشة الرئيسية على متصفّح Safari للأجهزة الجوّالة، يؤدي ذلك إلى إنشاء حاوية تخزين جديدة، ولا تتم مشاركة أي محتوى بين التطبيق المتوافق مع تقنية الويب التقدّمي ومتصفّح Safari للأجهزة الجوّالة. بعد بلوغ الحصة لتطبيق ويب تقدّمي مثبّت، لن تتوفر أي طريقة لطلب مساحة تخزين إضافية.
في السابق، إذا تجاوز موقع إلكتروني حدًا معيّنًا من البيانات المخزّنة، كان المتصفّح يطلب من المستخدم منح الإذن لاستخدام المزيد من البيانات. على سبيل المثال، إذا كان المصدر يستخدم أكثر من 50 ميغابايت، سيطلب المتصفّح من المستخدم السماح له بتخزين ما يصل إلى 100 ميغابايت، ثم سيطلب منه مرة أخرى السماح له بتخزين 50 ميغابايت إضافية.
في الوقت الحالي، لن تطلب معظم المتصفّحات الحديثة من المستخدم الموافقة على ذلك، وستسمح لأي موقع إلكتروني باستخدام ما يصل إلى حصته المحدّدة. يبدو أنّ الاستثناء هو Safari، الذي يُرسل إعلامًا عند تجاوز حصة مساحة التخزين، ويطلب الإذن لزيادة الحصة المخصّصة. إذا حاول مصدر استخدام أكثر من الحصة المخصّصة له، ستتعذّر محاولات كتابة البيانات التالية.
كيف يمكنني الاطّلاع على مساحة التخزين المتوفّرة؟
في العديد من المتصفّحات، يمكنك استخدام واجهة برمجة التطبيقات StorageManager API لتحديد مقدار مساحة التخزين المتوفّرة للموقع الإلكتروني وحجم مساحة التخزين التي يستخدمها. فهي تعرض إجمالي عدد وحدات البايت المستخدمة في IndexedDB وواجهة برمجة تطبيقات ذاكرة التخزين المؤقت، وتتيح إمكانية حساب مساحة التخزين المتبقية المتاحة.
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.`);
}
يجب اكتشاف أخطاء تجاوز الحصة المسموح بها (انظر أدناه). في بعض الحالات، من الممكن أن تتجاوز الحصة المتاحة المساحة الفعلية المتوفّرة.
فحص
أثناء التطوير، يمكنك استخدام "أدوات المطوّر" في المتصفّح لفحص أنواع مساحة التخزين المختلفة ومحو جميع البيانات المخزّنة.
تمت إضافة ميزة جديدة في الإصدار 88 من Chrome تتيح لك إلغاء حصة مساحة التخزين الخاصة بالموقع الإلكتروني في "مساحة التخزين". تتيح لك هذه الميزة محاكاة أجهزة مختلفة واختبار سلوك تطبيقاتك في سيناريوهات قلة مساحة القرص. انتقِل إلى التطبيق ثم مساحة التخزين، فعِّل مربّع الاختيار محاكاة مساحة التخزين المخصّصة، وأدخِل أي رقم صالح للقيام بمحاولة محاكاة مساحة التخزين.
أثناء العمل على هذا الدليل، كتبت أداة بسيطة لمحاولة استخدام أكبر قدر ممكن من مساحة التخزين بسرعة. وهي طريقة سريعة لتجربة آليات تخزين مختلفة، والاطّلاع على ما يحدث عند استخدام كل حصتك.
كيف يمكن التعامل مع تجاوز الحصة؟
ما الذي يجب فعله عند تجاوز مساحة التخزين المتوفّرة؟ والأهم من ذلك، يجب
اكتشاف أخطاء الكتابة ومعالجتها دائمًا، سواء كانت QuotaExceededError
أو
أي شيء آخر. بعد ذلك، حدِّد كيفية التعامل مع هذه البيانات استنادًا إلى تصميم تطبيقك.
على سبيل المثال، يمكنك حذف المحتوى الذي لم يتم الوصول إليه منذ فترة طويلة أو إزالة data استنادًا إلى الحجم أو توفير طريقة للمستخدمين لاختيار ما يريدون حذفه.
يُعرِض كلّ من IndexedDB وCache API خطأ DOMError
باسم
QuotaExceededError
عند تجاوز الحصة المتاحة.
IndexedDB
إذا تجاوز المصدر حصته، ستؤدي محاولات الكتابة إلى IndexedDB إلى
تعذُّر. سيتمّ استدعاء معالِج onabort()
للمعاملة، مع تمرير حدث.
سيتضمّن الحدث DOMException
في سمة الخطأ. سيؤدي التحقّق من
الخطأ 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 وما إلى ذلك) تلقائيًا ضمن فئة "أفضل جهد"، ما يعني أنّه ما لم يطلب الموقع الإلكتروني مساحة تخزين دائمة، قد يُخرج المتصفّح بيانات الموقع الإلكتروني وفقًا لتقديره، على سبيل المثال، عندما تكون مساحة تخزين الجهاز منخفضة.
في ما يلي سياسة الإخلاء في حال بذل أقصى الجهود:
- ستبدأ المتصفّحات المستندة إلى Chromium في إزالة البيانات عندما ينفد المتصفّح من المساحة، ما يؤدي إلى محو جميع بيانات المواقع الإلكترونية من المصدر الأقل استخدامًا مؤخرًا أولاً، ثم المصدر التالي، إلى أن يتوقف المتصفّح عن تجاوز الحدّ المسموح به.
- سيبدأ Firefox في إزالة البيانات عند امتلاء مساحة القرص المتاحة، ويزيل أولاً جميع بيانات المواقع الإلكترونية من المصدر الأقل استخدامًا مؤخرًا، ثم يليه المصدر التالي، إلى أن يتوقف المتصفّح عن تجاوز الحدّ المسموح به.
- لم يستبعد Safari البيانات في السابق، ولكنه نفّذ مؤخرًا حدًّا أقصى لمدة سبعة أيام على جميع مساحات التخزين القابلة للكتابة عليها (انظر أدناه).
بدءًا من نظامي التشغيل iOS وiPadOS 13.4 وSafari 13.1 على نظام التشغيل macOS، يتم فرض حد أقصى لمدة سبعة أيام على جميع مساحات التخزين القابلة لكتابة النصوص البرمجية، بما في ذلك IndexedDB وتسجيل مشغّلي الخدمات وواجهة برمجة تطبيقات ذاكرة التخزين المؤقت. وهذا يعني أنّ Safari سيزيل كل المحتوى من ذاكرة التخزين المؤقت بعد سبعة أيام من استخدام Safari إذا لم يتفاعل المستخدم مع الموقع الإلكتروني. لا تنطبق سياسة الاستبعاد هذه على التطبيقات المُثبَّتة المتوافقة مع الويب التي تمت إضافتها إلى الشاشة الرئيسية. راجع الحظر الكامل لملفات تعريف الارتباط للجهات الخارجية وغير ذلك على مدونة WebKit للحصول على التفاصيل الكاملة.
حِزم التخزين
تتمثل الفكرة الأساسية لواجهة برمجة التطبيقات Storage Buckets API في منح المواقع الإلكترونية إمكانية إنشاء حِزم تخزين متعددة، حيث يمكن للمتصفّح اختيار حذف كل حزمة بشكل مستقل عن الحِزم الأخرى. يتيح ذلك للمطوّرين تحديد أولوية إزالة البيانات للتأكّد من عدم حذف البيانات الأكثر أهمية.
ميزة إضافية: سبب استخدام حزمة برمجية لـ IndexedDB
IndexedDB هي واجهة برمجة تطبيقات من المستوى الأدنى تتطلّب إعدادًا مهمًا قبل الاستخدام، ما قد يكون مزعجًا بشكل خاص لتخزين البيانات منخفضة التعقيد. وعلى عكس معظم واجهات برمجة التطبيقات الحديثة التي تستند إلى الوعد، تستند هذه الواجهة إلى الأحداث. تخفي برامج تضمين التعهد، مثل idb لـ IndexedDB، بعض الميزات الفعّالة، ولكن الأهم من ذلك أنها تخفي الأجهزة المعقدة (مثل المعاملات وإعداد إصدارات المخططات) التي تتضمنها مكتبة IndexedDB.
ميزة إضافية: SQLite Wasm
بعد إيقاف لغة الاستعلامات البنيوية (SQL) على الويب نهائيًا وإزالتها من Chrome، تعاونت Google مع القائمين على صيانة قاعدة بيانات SQLite الرائجة لتقديم بديل لـ Web SQL يستند إلى SQLite. يمكنك الاطّلاع على SQLite Wasm في المتصفّح المدعوم بنظام الملف الخاص Origin للحصول على تفاصيل حول كيفية استخدامه.
الخاتمة
لقد ولت أيام مساحة التخزين المحدودة وطلب المستخدمين تخزين المزيد من البيانات. يمكن للمواقع الإلكترونية تخزين جميع الموارد والبيانات التي تحتاج إليها بفعالية. باستخدام واجهة برمجة التطبيقات StorageManager API، يمكنك معرفة المساحة المتوفّرة لك والمساحة التي استخدمتها. وباستخدام مساحة التخزين الثابتة، يمكنك حمايتها من الاستبعاد ما لم يزيلها المستخدم.
مراجع إضافية
شكرًا
نشكر بشكل خاص "جاريد غودمان" و"فيل والتون" و"إييجي كاتامورا" و"دانيال ميرفي" و"داروين هوانغ" و"جوش بيل" و"مارين كرويسلبرينك" و"فيكتور كوستان" على مراجعة هذا الدليل. نشكر "إيجي كيتامورا" و"أدي عثماني" و"مارك كوهين" الذين كتبوا المقالات الأصلية التي يستند إليها هذا المقال. كتب إيجي أداة مفيدة تسمى إساءة استخدام مساحة التخزين في المتصفح وكانت مفيدة في التحقق من السلوك الحالي. يتيح لك هذا الإجراء تخزين أكبر قدر ممكن من البيانات والاطّلاع علىحدود التخزين في المتصفّح. نشكر "فرانسوا بافورت" الذي أجرى البحث المتعمّق في Safari لمعرفة حدود مساحة التخزين، ونشكر "توماس شتاينر" على إضافة معلومات عن نظام الملفات الخاص الأصلي وحِزم التخزين وSQLite Wasm، بالإضافة إلى تعديل شامل على المحتوى في عام 2024.
الصورة الرئيسية من إنشاء Guillaume Bolduc على Unsplash.