لإنشاء تجربة قوية بلا إنترنت، يجب أن يتضمّن تطبيق الويب التقدّمي إدارة التخزين. في فصل التخزين المؤقت، تعلّمت أنّ ذاكرة التخزين المؤقت هي أحد الخيارات لحفظ البيانات على الجهاز. في هذا الفصل، سنوضّح لك كيفية إدارة البيانات غير المتصلة بالإنترنت، بما في ذلك استمرار البيانات والحدود والأدوات المتاحة.
مساحة التخزين
لا تقتصر سعة التخزين على الملفات ومواد العرض، بل يمكن أن تشمل أنواعًا أخرى من البيانات. تتوفّر واجهات برمجة التطبيقات التالية للتخزين على الجهاز في جميع المتصفحات المتوافقة مع تطبيقات الويب التقدّمية:
- IndexedDB: خيار لتخزين عناصر NoSQL للبيانات المنظَّمة والبيانات الثنائية الكبيرة (blob).
- WebStorage: طريقة لتخزين أزواج السلاسل الرئيسية/القيمة، باستخدام مساحة التخزين المحلية أو مساحة تخزين الجلسة لا تتوفّر هذه الميزة في سياق مشغّل الخدمات. هذه الواجهة متزامنة، لذا لا يُنصح باستخدامها لتخزين البيانات المعقّدة.
- مساحة ذاكرة التخزين المؤقت: كما هو موضّح في وحدة التخزين المؤقت.
يمكنك إدارة مساحة تخزين جميع الأجهزة باستخدام Storage Manager API على الأنظمة الأساسية المتوافقة. توفّر واجهة برمجة التطبيقات Cache Storage API وIndexedDB إمكانية الوصول غير المتزامن إلى مساحة التخزين الثابتة لتطبيقات الويب التقدّمية، ويمكن الوصول إليها من سلسلة التعليمات الرئيسية وعاملَي الويب والخدمة. يلعب كلاهما دورًا أساسيًا في جعل تطبيقات الويب التقدّمية تعمل بشكل موثوق عندما تكون الشبكة غير مستقرة أو غير متوفرة. ولكن متى يجب استخدام كل منهما؟
استخدِم واجهة برمجة التطبيقات Cache Storage API لموارد الشبكة، أي العناصر التي يمكنك الوصول إليها من خلال طلبها عبر عنوان URL، مثل HTML وCSS وJavaScript والصور والفيديوهات والملفات الصوتية.
استخدِم IndexedDB لتخزين البيانات المنظَّمة. ويشمل ذلك البيانات التي يجب أن تكون قابلة للبحث أو الدمج بطريقة مشابهة لـ NoSQL، أو بيانات أخرى مثل البيانات الخاصة بالمستخدم التي لا تتطابق بالضرورة مع طلب عنوان URL. يُرجى العِلم أنّ IndexedDB غير مصمَّمة للبحث عن النص الكامل.
IndexedDB
لاستخدام IndexedDB، عليك أولاً فتح قاعدة بيانات. يؤدي ذلك إلى إنشاء قاعدة بيانات جديدة في حال عدم توفّر واحدة.
IndexedDB هي واجهة برمجة تطبيقات غير متزامنة، ولكنها تستخدم دالة ردّ نداء بدلاً من عرض وعد. يستخدم المثال التالي مكتبة idb الخاصة بـ Jake Archibald، وهي عبارة عن برنامج صغير لتغليف Promise في IndexedDB. لا يلزم استخدام مكتبات مساعدة لاستخدام IndexedDB، ولكن إذا كنت تريد استخدام بنية Promise، يمكنك استخدام مكتبة idb
.
ينشئ المثال التالي قاعدة بيانات لتخزين وصفات الطهي.
إنشاء قاعدة بيانات وفتحها
لفتح قاعدة بيانات، اتّبِع الخطوات التالية:
- استخدِم الدالة
openDB
لإنشاء قاعدة بيانات IndexedDB جديدة باسمcookbook
. بما أنّ قواعد بيانات IndexedDB تتضمّن إصدارات، عليك زيادة رقم الإصدار كلّما أجريت تغييرات على بنية قاعدة البيانات. المَعلمة الثانية هي إصدار قاعدة البيانات. في المثال، تم ضبط القيمة على 1. - يتم تمرير عنصر تهيئة يحتوي على دالة ردّ اتصال
upgrade()
إلىopenDB()
. يتم استدعاء دالة ردّ الاتصال عند تثبيت قاعدة البيانات للمرة الأولى أو عند ترقيتها إلى إصدار جديد. هذه الدالة هي المكان الوحيد الذي يمكن أن تحدث فيه الإجراءات. قد تتضمّن الإجراءات إنشاء مخازن عناصر جديدة (البُنى التي تستخدمها 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
.
لنلقِ نظرة على مستودع العناصر الذي تم إنشاؤه للتو. بعد إضافة وصفات إلى مستودع العناصر وفتح "أدوات المطوّرين" على المتصفّحات المستندة إلى Chromium أو "أداة فحص الويب" على 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]);
}
أداة إدارة مساحة التخزين
من المهم بشكل خاص معرفة كيفية إدارة مساحة التخزين في تطبيق الويب التقدّمي لتخزين استجابات الشبكة وبثها بشكل صحيح.
تتم مشاركة سعة التخزين بين جميع خيارات التخزين، بما في ذلك "تخزين ذاكرة التخزين المؤقت" وIndexedDB وWeb Storage وحتى ملف عامل الخدمة والملفات التابعة له.
ومع ذلك، يختلف مقدار مساحة التخزين المتاحة من متصفّح إلى آخر. من غير المحتمل أن ينفد هذا الحدّ، إذ يمكن للمواقع الإلكترونية تخزين ميغابايت وحتى غيغابايت من البيانات على بعض المتصفّحات. على سبيل المثال، يتيح Chrome للمتصفّح استخدام ما يصل إلى% 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، يمكنك الاطّلاع على حصة موقعك الإلكتروني ومقدار مساحة التخزين المستخدَمة مقسّمة حسب العناصر التي تستخدمها، وذلك من خلال فتح قسم مساحة التخزين في علامة التبويب التطبيق.
لا يوفّر المتصفّحان Firefox وSafari شاشة ملخّص للاطّلاع على جميع حصص التخزين والاستخدام للمصدر الحالي.
الاحتفاظ بالبيانات
يمكنك طلب مساحة تخزين ثابتة من المتصفّح على المنصات المتوافقة لتجنُّب إخلاء البيانات تلقائيًا بعد عدم النشاط أو عند الضغط على مساحة التخزين. وفي حال منح الإذن، لن يحذف المتصفّح البيانات من مساحة التخزين أبدًا. تشمل هذه الحماية تسجيل عامل الخدمة وقواعد بيانات 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()
. يطلب Firefox الإذن من المستخدم لاستخدام مساحة التخزين الدائمة. تسمح المتصفّحات المستندة إلى Chromium بالاحتفاظ بالبيانات أو ترفض ذلك استنادًا إلى قاعدة إرشادية لتحديد أهمية المحتوى للمستخدم. أحد معايير Google Chrome هو، على سبيل المثال، تثبيت تطبيقات الويب التقدّمية. إذا ثبَّت المستخدم رمزًا لتطبيق الويب التقدّمي في نظام التشغيل، قد يمنح المتصفّح مساحة تخزين ثابتة.
المتصفّحات المتوافقة مع واجهة برمجة التطبيقات
مساحة التخزين على الويب
File System Access
مدير مساحة التخزين