دليل المبتدئين لاستخدام ذاكرة التخزين المؤقت للتطبيق

مقدمة

تتزايد أهمية إمكانية الوصول إلى التطبيقات المستندة إلى الويب في وضع عدم الاتصال. نعم، يمكن لجميع المتصفحات تخزين الصفحات والموارد مؤقتًا لفترات طويلة إذا طُلب منك ذلك، ولكن يستطيع المتصفّح إخراج عناصر فردية من ذاكرة التخزين المؤقت في أي وقت لتوفير مساحة لأشياء أخرى. يعالج HTML5 بعض الإزعاج الناتج عن عدم اتصالك بالإنترنت باستخدام واجهة ApplicationCache. يؤدي استخدام واجهة ذاكرة التخزين المؤقت إلى منح التطبيق ثلاث مزايا:

  1. التصفّح بلا اتصال بالإنترنت: يمكن للمستخدمين التنقّل في موقعك الإلكتروني بالكامل عندما يكونون غير متصلين بالإنترنت.
  2. السرعة: تأتي الموارد مباشرةً من القرص، ولا تنتقل إلى الشبكة.
  3. المرونة: إذا تعطل موقعك الإلكتروني بسبب "الصيانة" (كما هو الحال في حال تعطل أحد الأشخاص لكل شيء عن طريق الخطأ)، سيحصل المستخدمون على التجربة بلا اتصال بالإنترنت

تتيح ذاكرة التخزين المؤقت للتطبيق (أو 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 يعمل في المتصفّحات القديمة وIE11.

بنية ملف البيان

البيان هو ملف منفصل ترتبط به عبر سمة البيان في عنصر 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

الأسطر التي تبدأ بعلامة "nofollow" هي سطور تعليقات، ولكنها يمكن أن تخدم أيضًا غرضًا آخر. لا يتم تحديث ذاكرة التخزين المؤقت للتطبيق إلا عند تغيير ملف البيان. على سبيل المثال، إذا عدّلت مورد صورة أو غيّرت إحدى وظائف JavaScript، لن تتم إعادة تخزين هذه التغييرات مؤقتًا. يجب تعديل ملف البيان نفسه لإعلام المتصفِّح بتحديث الملفات المخزَّنة مؤقتًا.

تجنَّب استخدام طابع زمني يتم تحديثه باستمرار أو سلسلة عشوائية لفرض إجراء التحديثات في كل مرة. يتم التحقق من البيان مرتين أثناء التحديث، مرة في البداية ومرة بعد تحديث جميع الملفات المخزنة مؤقتًا. إذا تغيّر البيان أثناء التحديث، من المحتمل أن يكون المتصفِّح قد جلب بعض الملفات من إصدار معيّن وملفات أخرى من إصدار آخر، لذا لا يطبّق ذاكرة التخزين المؤقت ويعيد المحاولة لاحقًا.

على الرغم من أنّه يتم تعديل ذاكرة التخزين المؤقت، لن يستخدم المتصفّح هذه الملفات إلى أن تتم إعادة تحميل الصفحة، لأنّ هذه التحديثات تتمّ بعد تحميل الصفحة من الإصدار الحالي لذاكرة التخزين المؤقت.

يمكن أن يحتوي البيان على ثلاثة أقسام مختلفة: CACHE وNETWORK وFALLBACK.

CACHE:
هذا هو القسم التلقائي للإدخالات. سيتم بشكل صريح تخزين الملفات المدرَجة ضمن هذا العنوان (أو بعد CACHE MANIFEST مباشرةً) بعد تنزيلها للمرّة الأولى. NETWORK:
قد تكون الملفات المدرَجة في هذا القسم من الشبكة إذا لم تكن متوفّرة في ذاكرة التخزين المؤقت، وإلا لن يتم استخدام الشبكة، حتى إذا كان المستخدم متصلاً بالإنترنت. يمكنك هنا إضافة عناوين URL محددة إلى القائمة البيضاء، أو ببساطة الرمز ""، ما يسمح بجميع عناوين URL. تحتاج معظم المواقع الإلكترونية إلى "". FALLBACK:
قسم اختياري يحدّد الصفحات الاحتياطية في حال تعذّر الوصول إلى المورد. معرف الموارد المنتظم (URI) الأول هو المورد، والثاني هو المورد الاحتياطي المستخدم إذا فشل طلب الشبكة أو حدوث أخطاء. يجب أن يكون مصدر كل من معرِّفات الموارد المنتظمة (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

تحديث ذاكرة التخزين المؤقت

بمجرد عدم اتصال التطبيق بالإنترنت، يظل مخزَّنًا مؤقتًا إلى أن يحدث أحد الأمور التالية:

  1. يمحو المستخدم مساحة تخزين البيانات في المتصفّح على موقعك الإلكتروني.
  2. تم تعديل ملف البيان. ملاحظة: لا يعني تعديل ملف مُدرَج في ملف البيان أن المتصفِّح سيعيد تخزين هذا المورد مؤقتًا. يجب تغيير ملف البيان نفسه.

حالة ذاكرة التخزين المؤقت

إنّ الكائن 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);

إذا تعذّر تنزيل ملف البيان أو مورد محدد فيه، سيتعذّر إجراء التحديث بالكامل. وسيستمر المتصفّح في استخدام ذاكرة التخزين المؤقت للتطبيقات القديمة في حال حدوث هذا الخطأ.

المراجع