مقدمة
تزداد أهمية إمكانية استخدام التطبيقات المستندة إلى الويب بلا اتصال بالإنترنت. نعم، يمكن لجميع المتصفّحات تخزين الصفحات والموارد مؤقتًا لفترات طويلة إذا تمّت مطالبتها بذلك، ولكن يمكن للمتصفّح إزالة عناصر فردية من ذاكرة التخزين المؤقت في أي وقت لإفساح المجال لعناصر أخرى. يعالج HTML5 بعض المشاكل المزعجة التي تواجهك عند عدم الاتصال بالإنترنت باستخدام واجهة ApplicationCache. يوفّر استخدام واجهة ذاكرة التخزين المؤقت لتطبيقك ثلاث مزايا:
- التصفح بلا اتصال بالإنترنت: يمكن للمستخدمين التنقّل في موقعك الإلكتروني بالكامل في حال عدم اتصالهم بالإنترنت.
- السرعة: يتم الحصول على الموارد مباشرةً من القرص، بدون الحاجة إلى الاتصال بالشبكة.
- المرونة: إذا تعطّل موقعك الإلكتروني "للصيانة" (أي إذا تعطّل كل شيء عن طريق الخطأ)، سيحصل المستخدمون على تجربة بلا إنترنت.
تسمح ذاكرة التخزين المؤقت للتطبيق (أو Appcache) لمطوّر البرامج بتحديد الملفات التي يجب أن يخزّنها المتصفّح في ذاكرة التخزين المؤقت وإتاحتها للمستخدمين بلا اتصال بالإنترنت. سيتم تحميل تطبيقك ويعمل بشكل صحيح، حتى إذا ضغط المستخدم على زر إعادة التحميل أثناء عدم الاتصال بالإنترنت.
ملف بيان ذاكرة التخزين المؤقت
ملف بيان ذاكرة التخزين المؤقت هو ملف نصي بسيط يسرد الموارد التي يجب أن يخزنها المتصفّح في ذاكرة التخزين المؤقت للوصول إليها بلا اتصال بالإنترنت.
الإشارة إلى ملف بيان
لتفعيل ذاكرة التخزين المؤقت للتطبيق في أحد التطبيقات، يجب تضمين سمة البيان في علامة html
الخاصة بالمستند:
<html manifest="example.appcache">
...
</html>
يجب تضمين سمة manifest
في كل صفحة من صفحات تطبيق الويب
التي تريد تخزينها مؤقتًا. لا يخزِّن المتصفّح أي صفحة إذا لم تتضمّن
السمة manifest
(ما لم يتم إدراجها بشكل صريح
في ملف البيان نفسه). وهذا يعني أنّ أي صفحة ينتقل إليها المستخدم والتي
تتضمّن manifest
ستتم إضافتها ضمنيًا إلى ذاكرة التخزين المؤقت للتطبيق.
وبالتالي، ليس عليك إدراج كل صفحة في البيان. وإذا أشارت الصفحة إلى بيان ما، وليست هناك طريقة لمنع تخزين هذه الصفحة مؤقتًا.
يمكنك الاطّلاع على عناوين URL التي تتحكّم فيها ذاكرة التخزين المؤقت للتطبيق من خلال الانتقال إلى about://://appcache-internals/ في Chrome. من هنا، يمكنك محو ذاكرة التخزين المؤقت وعرض الإدخالات. تتوفّر أدوات مطوّرين مشابهة في Firefox.
يمكن أن تشير سمة manifest
إلى عنوان URL مطلق أو مسار نسبي،
ولكن يجب أن يكون عنوان URL المطلق ضمن المصدر نفسه لتطبيق الويب.
يمكن أن يحتوي ملف البيان على أي امتداد ملف، ولكن يجب عرضه
باستخدام نوع mime الصحيح (راجِع المعلومات أدناه).
<html manifest="http://www.example.com/example.mf">
...
</html>
يجب عرض ملف البيان باستخدام نوع mime text/cache-manifest
.
قد تحتاج إلى إضافة نوع ملف مخصّص إلى خادم الويب أو إعدادات .htaccess
.
على سبيل المثال، لعرض نوع mime هذا في Apache، أضِف هذا السطر إلى ملف الإعدادات:
AddType text/cache-manifest .appcache
أو في ملف app.yaml في Google App Engine:
- url: /mystaticdir/(.*\.appcache)
static_files: mystaticdir/\1
mime_type: text/cache-manifest
upload: mystaticdir/(.*\.appcache)
تمّ إسقاط هذا الشرط من المواصفات منذ بعض الوقت، ولم يعُد مطلوبًا في أحدث إصدارات Chrome وSafari وFirefox، ولكنّك ستحتاج إلى mime-type لكي تعمل في المتصفّحات القديمة وInternet Explorer 11.
بنية ملف البيان
البيان هو ملف منفصل يمكنك الربط به عبر سمة البيان في عنصر html. يبدو البيان البسيط على النحو التالي:
CACHE MANIFEST
index.html
stylesheet.css
images/logo.png
scripts/main.js
http://cdn.example.com/scripts/main.js
سيخزّن هذا المثال أربعة ملفات في ذاكرة التخزين المؤقت على الصفحة التي تحدّد ملف البيان هذا.
في ما يلي بعض الملاحظات:
- سلسلة
CACHE MANIFEST
هي السطر الأول وهي مطلوبة. - يمكن أن تكون الملفات من نطاق آخر
- تفرض بعض المتصفّحات قيودًا على مقدار مساحة التخزين المتاحة
لتطبيقك. في Chrome مثلاً، يستخدم AppCache مجموعة مشتركة من مساحة التخزين المؤقتة
التي يمكن أن تشاركها واجهات برمجة التطبيقات الأخرى التي تعمل بلا إنترنت. إذا كنت تكتب تطبيقًا لـ سوق Chrome الإلكتروني، يزيل استخدام الرمز
unlimitedStorage
هذا القيد. - إذا كان البيان نفسه يعرض 404 أو 410، سيتم حذف ذاكرة التخزين المؤقت.
- إذا تعذّر تنزيل البيان أو أحد الموارد المحدّدة فيه، تتعذّر عملية تعديل ذاكرة التخزين المؤقت بالكامل. سيواصل المتصفّح استخدام ملف التخزين المؤقّت القديم للتطبيق في حال تعذّر تحميل البيانات.
لنلقِ نظرة على مثال أكثر تعقيدًا:
CACHE MANIFEST
# 2010-06-18:v2
# Explicitly cached 'master entries'.
CACHE:
/favicon.ico
index.html
stylesheet.css
images/logo.png
scripts/main.js
# Resources that require the user to be online.
NETWORK:
*
# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
# offline.html will be served in place of all other .html files
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
الأسطر التي تبدأ بـ "#" هي أسطر تعليقات، ولكن يمكن أن تخدم أيضًا غرضًا آخر. لا يتم تعديل ذاكرة التخزين المؤقت للتطبيق إلا عند تغيير ملف البيان الخاص به. على سبيل المثال، إذا عدّلت مورد صورة أو غيّرت وظيفة JavaScript، لن تتم إعادة تخزين هذه التغييرات مؤقتًا. يجب تعديل ملف البيان نفسه لإعلام المتصفّح بإعادة تحميل الملفات المخزّنة مؤقتًا.
تجنَّب استخدام طابع زمني يتم تحديثه باستمرار أو سلسلة عشوائية لفرض إجراء التحديثات في كل مرة. يتم التحقّق من البيان مرّتين أثناء التحديث، مرّة في البداية ومرّة بعد تعديل جميع الملفات المخزّنة مؤقتًا. إذا تغيّر البيان أثناء التحديث، من المحتمل أنّ المتصفّح جلب بعض الملفات من إصدار وملفّات أخرى من إصدار آخر، لذا لا يطبّق ذاكرة التخزين المؤقت ويعيد المحاولة لاحقًا.
على الرغم من أنه يتم تحديث ذاكرة التخزين المؤقت، لن يستخدم المتصفح هذه الملفات إلى أن يتم تحديث الصفحة، نظرًا لأن التحديثات تحدث بعد تحميل الصفحة من الإصدار الحالي لذاكرة التخزين المؤقت.
يمكن أن يتضمّن البيان ثلاثة أقسام مختلفة: CACHE
وNETWORK
وFALLBACK
.
CACHE:
- هذا هو القسم التلقائي للإدخالات. سيتم تخزين الملفات المدرَجة ضمن هذا العنوان (أو بعد الرمز
CACHE MANIFEST
مباشرةً) بشكل صريح في الذاكرة المؤقتة بعد تنزيلها للمرة الأولى.NETWORK:
- قد تأتي الملفات المدرَجة في هذا القسم من الشبكة إذا لم تكن متوفّرة في ذاكرة التخزين المؤقت، وإلا لن يتم استخدام الشبكة حتى إذا كان المستخدم متصلاً بالإنترنت. يمكنك إضافة عناوين URL محدَّدة إلى القائمة البيضاء هنا، أو ببساطة ""، ما يسمح بجميع عناوين URL. تحتاج معظم المواقع الإلكترونية إلى الرمز "".
FALLBACK:
- قسم اختياري يحدّد الصفحات الاحتياطية في حال تعذّر الوصول إلى أحد الموارد. عنوان URI الأول هو المورد، والثاني هو الاحتياطي المستخدم إذا فشل طلب الشبكة أو حدثت أخطاء. يجب أن يكون كلا معرّفي الموارد المنتظمَين من المصدر نفسه لملف البيان. ويمكنك تسجيل عناوين URL محدّدة ولكن أيضًا بادئات عناوين URL. سيسجّل "images/large/" حالات الفشل من عناوين URL مثل "images/large/whatever/img.jpg".
يحدد البيان التالي صفحة "التقاط الكل" (offline.html) التي سيتم عرضها عندما يحاول المستخدم الوصول إلى جذر الموقع الإلكتروني في وضع عدم الاتصال. وتوضّح أيضًا أنّ جميع الموارد الأخرى (مثل الموارد المتوفّرة على موقع إلكتروني عن بُعد) تتطلب اتصالاً بالإنترنت.
CACHE MANIFEST
# 2010-06-18:v3
# Explicitly cached entries
index.html
css/style.css
# offline.html will be displayed if the user is offline
FALLBACK:
/ /offline.html
# All other resources (e.g. sites) require the user to be online.
NETWORK:
*
# Additional resources to cache
CACHE:
images/logo1.png
images/logo2.png
images/logo3.png
تعديل ذاكرة التخزين المؤقت
بعد أن يصبح التطبيق بلا اتصال بالإنترنت، يظل مخزَّنًا مؤقتًا إلى أن يحدث أحد الإجراءات التالية:
- يمحو المستخدم مساحة تخزين البيانات في المتصفّح لموقعك الإلكتروني.
- تم تعديل ملف البيان. ملاحظة: لا يعني تعديل ملف مُدرَج في البيان أنّ المتصفّح سيعيد تخزين هذا المورد مؤقتًا. يجب تغيير ملف البيان نفسه.
حالة ذاكرة التخزين المؤقت
العنصر window.applicationCache
هو الوصول الآلي إلى ذاكرة التخزين المؤقت للتطبيقات في المتصفّح.
يكون سمة status
مفيدة للتحقّق من الحالة الحالية لذاكرة التخزين المؤقت:
var appCache = window.applicationCache;
switch (appCache.status) {
case appCache.UNCACHED: // UNCACHED == 0
return 'UNCACHED';
break;
case appCache.IDLE: // IDLE == 1
return 'IDLE';
break;
case appCache.CHECKING: // CHECKING == 2
return 'CHECKING';
break;
case appCache.DOWNLOADING: // DOWNLOADING == 3
return 'DOWNLOADING';
break;
case appCache.UPDATEREADY: // UPDATEREADY == 4
return 'UPDATEREADY';
break;
case appCache.OBSOLETE: // OBSOLETE == 5
return 'OBSOLETE';
break;
default:
return 'UKNOWN CACHE STATUS';
break;
};
للتحقّق من توفّر تحديثات للبيان آليًا، يجب أولاً استدعاء applicationCache.update()
.
سيؤدي ذلك إلى محاولة تعديل ذاكرة التخزين المؤقت للمستخدم (ما يتطلّب تغيير ملف البيان).
أخيرًا، عندما يكون applicationCache.status
في الحالة UPDATEREADY
، يؤديapplicationCache.swapCache()
إلى تبديل ذاكرة التخزين المؤقت القديمة بذاكرة التخزين المؤقت الجديدة.
var appCache = window.applicationCache;
appCache.update(); // Attempt to update the user's cache.
...
if (appCache.status == window.applicationCache.UPDATEREADY) {
appCache.swapCache(); // The fetch was successful, swap in the new cache.
}
.
والخبر السار هو أنّه يمكنك برمجة هذه العملية. لإطلاع المستخدمين على أحدث إصدار من موقعك الإلكتروني، عليك ضبط أداة استماع لمراقبة الحدث updateready
عند تحميل الصفحة:
// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
} else {
// Manifest didn't changed. Nothing new to server.
}
}, false);
}, false);
أحداث AppCache
كما هو متوقّع، يتم عرض أحداث إضافية لمراقبة حالة ذاكرة التخزين المؤقت. يُطلق المتصفّح أحداثًا لأمور مثل مستوى التقدّم في التنزيل وتعديل ذاكرة التخزين المؤقت للتطبيق وشروط الأخطاء. يعمل المقتطف التالي على إعداد أدوات معالجة الأحداث لكل نوع من أحداث ذاكرة التخزين المؤقت:
function handleCacheEvent(e) {
//...
}
function handleCacheError(e) {
alert('Error: Cache failed to update!');
};
// Fired after the first cache of the manifest.
appCache.addEventListener('cached', handleCacheEvent, false);
// Checking for an update. Always the first event fired in the sequence.
appCache.addEventListener('checking', handleCacheEvent, false);
// An update was found. The browser is fetching resources.
appCache.addEventListener('downloading', handleCacheEvent, false);
// The manifest returns 404 or 410, the download failed,
// or the manifest changed while the download was in progress.
appCache.addEventListener('error', handleCacheError, false);
// Fired after the first download of the manifest.
appCache.addEventListener('noupdate', handleCacheEvent, false);
// Fired if the manifest file returns a 404 or 410.
// This results in the application cache being deleted.
appCache.addEventListener('obsolete', handleCacheEvent, false);
// Fired for each resource listed in the manifest as it is being fetched.
appCache.addEventListener('progress', handleCacheEvent, false);
// Fired when the manifest resources have been newly redownloaded.
appCache.addEventListener('updateready', handleCacheEvent, false);
إذا تعذّر تنزيل ملف البيان أو أحد الموارد المحدّدة فيه، تعذّر إجراء التحديث بالكامل. وسيواصل المتصفّح استخدام ذاكرة التخزين المؤقت القديمة للتطبيق في حال حدوث هذا الخطأ.
المراجع
- مواصفات واجهة برمجة التطبيقات ApplicationCache
- Application Cache هو أحمق: يتناول هذا المقال المشاكل المتعلقة بذاكرة التخزين المؤقت للتطبيق.