ميزة "التخزين المؤقت للصفحات"

تاريخ النشر: 25 مايو 2023، تاريخ آخر تعديل: 2 يوليو 2026

ميزة "التخزين المؤقت للصفحات" (أو bfcache) هي أداة تحسين للمتصفّح تتيح الانتقال الفوري للأمام أو للخلف. كما تُحسِّن تجربة التصفُّح بشكل كبير، ولا سيما للمستخدمين الذين لديهم شبكات أو أجهزة بطيئة.

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

توافُق المتصفح

تتضمّن جميع المتصفّحات الرئيسية ذاكرة تخزين مؤقت للصفحات السابقة/التالية، بما في ذلك Chrome منذ الإصدار 96 وFirefox وSafari.

أساسيات ميزة "التخزين المؤقت للصفحات"

باستخدام ميزة "التخزين المؤقت للصفحات"، بدلاً من إتلاف الصفحة عندما ينتقل المستخدم إلى صفحة أخرى، نؤجّل عملية الإتلاف ونوقف تنفيذ JavaScript مؤقتًا. إذا عاد المستخدم إلى الصفحة سريعًا، نعيد عرضها ونستأنف تنفيذ JavaScript. ويؤدي ذلك إلى انتقال المستخدم من صفحة إلى أخرى بشكل شبه فوري.

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

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

شاهِد هذا الفيديو الذي يعرض ميزة "التخزين المؤقت للصفحات" أثناء عملها لفهم مدى السرعة التي يمكن أن توفّرها في عمليات التنقّل:

يؤدي استخدام ميزة "التخزين المؤقت للصفحات" إلى تحميل الصفحات بسرعة أكبر بكثير أثناء التنقّل بين الصفحات السابقة واللاحقة.

في الفيديو، يكون المثال الذي يتضمّن ميزة bfcache أسرع بكثير من المثال الذي لا يتضمّنها.

لا تؤدي ميزة "التخزين المؤقت للصفحات" إلى تسريع عملية التنقّل فحسب، بل تقلّل أيضًا من استخدام البيانات، إذ لا يلزم تنزيل الموارد مرة أخرى.

تُظهر بيانات استخدام Chrome أنّ عملية واحدة من كل 10 عمليات تنقّل على أجهزة الكمبيوتر المكتبي وعملية واحدة من كل 5 عمليات على الأجهزة الجوّالة تكون إما رجوعًا إلى الخلف أو تقدمًا إلى الأمام. عند تفعيل ذاكرة التخزين المؤقت للخلف والأمام، يمكن للمتصفّحات إلغاء نقل البيانات والوقت المستغرَق في تحميل المليارات من صفحات الويب كل يوم.

طريقة عمل "ذاكرة التخزين المؤقت"

تختلف "ذاكرة التخزين المؤقت" التي تستخدمها ميزة "التخزين المؤقت للصفحات" عن ذاكرة التخزين المؤقت لبروتوكول HTTP التي تؤدي دورًا خاصًا في تسريع عمليات التنقّل المتكررة. فميزة "التخزين المؤقت للصفحات" هي عبارة عن لقطة للصفحة بأكملها في الذاكرة، بما في ذلك مجموعة بيانات JavaScript، بينما تحتوي ذاكرة التخزين المؤقت لبروتوكول HTTP على الردود فقط على الطلبات التي تم إجراؤها سابقًا. بما أنّه من النادر جدًا أن يتم تنفيذ جميع الطلبات اللازمة لتحميل صفحة من ذاكرة التخزين المؤقت HTTP، تكون الزيارات المتكررة باستخدام عمليات استعادة من ذاكرة التخزين المؤقت للصفحات (bfcache) أسرع دائمًا من عمليات التنقّل غير المستندة إلى ذاكرة التخزين المؤقت للصفحات (bfcache) حتى لو كانت محسّنة بشكل جيد.

يتضمّن تجميد الصفحة لإعادة تفعيلها لاحقًا بعض التعقيد من حيث أفضل طريقة للحفاظ على الرمز البرمجي قيد التنفيذ. على سبيل المثال، كيف تتعامل مع طلبات setTimeout() التي تم بلوغ المهلة المحدّدة لها بينما تكون الصفحة في ذاكرة التخزين المؤقت للخلف والأمام؟

والإجابة هي أنّ المتصفّحات توقِف أي مؤقّتات معلّقة أو وعود لم يتم حلّها للصفحات في ذاكرة التخزين المؤقت للخلف والأمام، بما في ذلك جميع المهام المعلقة تقريبًا في قوائم انتظار مهام JavaScript، وتستأنف معالجة المهام إذا تمت استعادة الصفحة من ذاكرة التخزين المؤقت للخلف والأمام.

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

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

ميزة "التخزين المؤقت للصفحات" وإطارات iframe

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

ومع ذلك، عند استعادة الإطار الرئيسي من ذاكرة التخزين المؤقت للصفحات (bfcache)، ستتم استعادة إطارات iframe المضمّنة كما كانت عندما دخلت الصفحة إلى ذاكرة التخزين المؤقت للصفحات.

يمكن أيضًا حظر الإطار الرئيسي من استخدام ميزة "التخزين المؤقت للصفحات" إذا كان إطار iframe مضمّن يستخدم واجهات برمجة تطبيقات تحظر ذلك. يمكن استخدام سياسة الأذونات التي تم ضبطها على الإطار الرئيسي أو استخدام سمات sandbox لتجنُّب ذلك.

ميزة "التخزين المؤقت للصفحات" والتطبيقات من صفحة واحدة

بما أنّ bfcache تعمل مع عمليات التنقّل التي يديرها المتصفّح، لا يمكن استخدامها مع "عمليات التنقّل السلسة" في تطبيق من صفحة واحدة (SPA). ومع ذلك، يمكن أن تساعد ذاكرة التخزين المؤقت للصفحات الخلفية عند الرجوع إلى تطبيق من صفحة واحدة بدلاً من إعادة تهيئة هذا التطبيق بالكامل من البداية.

واجهات برمجة التطبيقات لمراقبة ميزة "التخزين المؤقت للصفحات"

على الرغم من أنّ التخزين المؤقت والرجوع للأمام والخلف هو تحسين تجريه المتصفحات تلقائيًا، يظل من المهم أن يعرف المطوّرون متى يحدث ذلك حتى يتمكّنوا من تحسين صفحاتهم من أجله وتعديل أي مقاييس أو قياس للأداء وفقًا لذلك.

الأحداث الأساسية المستخدَمة لمراقبة ذاكرة التخزين المؤقت للصفحات السابقة/التالية هي أحداث انتقال الصفحة pageshow وpagehide، والتي تتوافق مع معظم المتصفّحات.

يتم أيضًا إرسال أحداث دورة حياة الصفحة الأحدث، أي freeze وresume، عندما تدخل الصفحات إلى ذاكرة التخزين المؤقت للخلف/الأمام أو تخرج منها، بالإضافة إلى بعض الحالات الأخرى، مثل عندما يتم تجميد علامة تبويب في الخلفية لتقليل استخدام وحدة المعالجة المركزية. لا تتوفّر هذه الأحداث إلا في المتصفّحات المستندة إلى Chromium.

مراقبة وقت استعادة صفحة من ذاكرة التخزين المؤقت للخلف والأمام

يتم تنشيط الحدث pageshow مباشرةً بعد الحدث load عند تحميل الصفحة في البداية وفي أي وقت تتم فيه استعادة الصفحة من التخزين المؤقت للصفحات. يحتوي الحدث pageshow على السمة persisted التي تكون قيمتها true إذا تمت استعادة الصفحة من ذاكرة التخزين المؤقت للصفحات (bfcache)، وfalse في الحالات الأخرى. يمكنك استخدام السمة persisted للتمييز بين عمليات تحميل الصفحات العادية وعمليات استعادة الصفحات من ذاكرة التخزين المؤقت للصفحات. على سبيل المثال:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    console.log('This page was restored from the bfcache.');
  } else {
    console.log('This page was loaded normally.');
  }
});

في المتصفّحات التي تتيح استخدام Page Lifecycle API، يتمّ تنشيط الحدث resume عند استعادة الصفحات من التخزين المؤقت للصفحات (مباشرةً قبل الحدث pageshow) وعندما يعيد المستخدم زيارة علامة تبويب مجمّدة في الخلفية. إذا أردت تعديل حالة الصفحة بعد تجميدها (بما في ذلك الصفحات في ذاكرة التخزين المؤقت للخلف والأمام)، يمكنك استخدام الحدث resume، ولكن إذا أردت قياس معدّل الوصول إلى ذاكرة التخزين المؤقت للخلف والأمام في موقعك الإلكتروني، عليك استخدام الحدث pageshow. في بعض الحالات، قد تحتاج إلى استخدام كليهما.

للحصول على تفاصيل حول أفضل الممارسات لقياس ميزة "التخزين المؤقت للصفحات"، اطّلِع على مقالة تأثير ميزة "التخزين المؤقت للصفحات" في الإحصاءات وقياس الأداء.

مراقبة وقت دخول الصفحة إلى ذاكرة التخزين المؤقت للخلف والأمام

يتم تنشيط الحدث pagehide إما عند إلغاء تحميل صفحة أو عندما يحاول المتصفّح وضعها في ذاكرة التخزين المؤقت للخلف والأمام.

يتضمّن حدث pagehide أيضًا السمة persisted. إذا كانت القيمة false، يمكنك التأكّد من أنّ الصفحة لن تدخل ذاكرة التخزين المؤقت للصفحات (bfcache). مع ذلك، persisted لا يضمن true أن يتم تخزين الصفحة مؤقتًا. هذا يعني أنّ المتصفّح ينوي تخزين الصفحة مؤقتًا، ولكن قد تكون هناك عوامل أخرى تجعل التخزين المؤقت مستحيلاً.

window.addEventListener('pagehide', (event) => {
  if (event.persisted) {
    console.log('This page *might* be entering the bfcache.');
  } else {
    console.log('This page will unload normally and be discarded.');
  }
});

وبالمثل، يتم تنشيط الحدث freeze مباشرةً بعد الحدث pagehide إذا كانت قيمة persisted هي true، ولكن هذا يعني فقط أنّ المتصفّح ينوي تخزين الصفحة مؤقتًا. ومع ذلك، قد يضطر إلى تجاهلها لعدة أسباب موضّحة لاحقًا.

تحسين صفحاتك لتكون متوافقة مع bfcache

لا يتم تخزين جميع الصفحات في ميزة "التخزين المؤقت للصفحات"، وحتى عندما يتم تخزين صفحة فيها، لن تبقى فيها إلى أجل غير مسمى. من الضروري أن يفهم المطوّرون العوامل التي تجعل الصفحات مؤهَّلة (وغير مؤهَّلة) لاستخدام bfcache من أجل زيادة معدّلات الوصول إلى ذاكرة التخزين المؤقت إلى أقصى حدّ.

توضّح الأقسام التالية أفضل الممارسات لزيادة احتمالية أن يتمكّن المتصفّح من تخزين صفحاتك مؤقتًا.

عدم استخدام الحدث unload مطلقًا

إنّ أهم طريقة لتحسين الأداء في bfcache على جميع المتصفّحات هي عدم استخدام الحدث unload مطلقًا. أبدًا!

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

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

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

على الأجهزة الجوّالة، سيحاول Chrome وSafari تخزين الصفحات مؤقتًا باستخدام أداة معالجة الأحداث unload لأنّ خطر حدوث مشاكل أقل بسبب عدم موثوقية الحدث unload على الأجهزة الجوّالة. يتعامل Firefox مع الصفحات التي تستخدم unload على أنّها غير مؤهَّلة للاستفادة من ميزة "التخزين المؤقت للصفحات"، باستثناء نظام التشغيل iOS، حيث تستخدم جميع المتصفّحات محرّك العرض WebKit، وبالتالي تتصرف مثل Safari.

بدلاً من استخدام الحدث unload، استخدِم الحدث pagehide. يتم تنشيط الحدث pagehide في جميع الحالات التي يتم فيها تنشيط الحدث unload، ويتم تنشيطه أيضًا عند وضع صفحة في ذاكرة التخزين المؤقت للخلف والأمام.

في الواقع، يتضمّن Lighthouse عملية تدقيق no-unload-listeners، والتي ستنبّه المطوّرين إذا أضافت أي لغة JavaScript على صفحاتهم (بما في ذلك تلك من المكتبات التابعة لجهات خارجية) أداة معالجة أحداث unload.

بسبب عدم موثوقيتها وتأثيرها في الأداء عند استخدام ذاكرة التخزين المؤقت للصفحات الخلفية، يسعى Chrome إلى إيقاف حدث unload نهائيًا.

استخدام سياسة الأذونات لمنع استخدام معالجات إلغاء التحميل على صفحة

يمكن للمواقع الإلكترونية التي لا تستخدم معالجات أحداث unload التأكّد من عدم إضافة هذه المعالجات باستخدام سياسة الأذونات.

Permissions-Policy: unload=()

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

إضافة beforeunload مستمعين بشكل مشروط فقط

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

على عكس حدث unload، هناك استخدامات مشروعة لحدث beforeunload. على سبيل المثال، عندما تريد تنبيه المستخدم بأنّه أجرى تغييرات لم يتم حفظها وسيتم فقدانها إذا غادر الصفحة. في هذه الحالة، ننصحك بإضافة أدوات معالجة beforeunload فقط عندما يكون لدى المستخدم تغييرات غير محفوظة، ثم إزالتها فورًا بعد حفظ التغييرات غير المحفوظة.

الإجراءات غير المُوصى بها
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
تضيف هذه التعليمة البرمجية أداة معالجة beforeunload بدون شروط.
الإجراءات الموصى بها
function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});
لا يضيف هذا الرمز المستمع beforeunload إلا عند الحاجة إليه (ويزيله عند عدم الحاجة إليه).

تقليل استخدام Cache-Control: no-store

Cache-Control: no-store هو عنوان HTTP يمكن لخوادم الويب ضبطه في الاستجابات التي توجّه المتصفّح إلى عدم تخزين الاستجابة في أي ذاكرة تخزين مؤقت لبروتوكول HTTP. يتم استخدامها للموارد التي تحتوي على معلومات حساسة عن المستخدمين، مثل الصفحات المحمية بكلمة مرور.

على الرغم من أنّ ميزة "التخزين المؤقت للصفحات" ليست ذاكرة تخزين مؤقت HTTP، إلا أنّه في السابق، عندما تم ضبط Cache-Control: no-store على مورد الصفحة نفسه (بدلاً من أي مورد فرعي)، اختارت المتصفحات عدم تخزين الصفحة في ذاكرة التخزين المؤقت للصفحات، لذا قد لا تكون أي صفحات تستخدم Cache-Control: no-store مؤهَّلة للاستفادة من ميزة "التخزين المؤقت للصفحات". نعمل حاليًا على تغيير هذا السلوك في Chrome بطريقة تحافظ على الخصوصية.

بما أنّ Cache-Control: no-store يقيّد أهلية الصفحة للاستفادة من ذاكرة التخزين المؤقت للصفحات (bfcache)، يجب ضبطه فقط على الصفحات التي تحتوي على معلومات حساسة لا يكون فيها التخزين المؤقت بأي شكل من الأشكال مناسبًا أبدًا.

بالنسبة إلى الصفحات التي تحتاج إلى عرض محتوى حديث دائمًا ولا يحتوي على معلومات حساسة، استخدِم Cache-Control: no-cache أو Cache-Control: max-age=0. توجّه هذه التوجيهات المتصفّح بإعادة التحقّق من صحة المحتوى قبل عرضه، ولا تؤثّر في أهلية الصفحة للاستفادة من ذاكرة التخزين المؤقت للخلف والأمام.

يُرجى العِلم أنّه عند استعادة صفحة من ذاكرة التخزين المؤقت للصفحات (bfcache)، تتم استعادتها من الذاكرة وليس من ذاكرة التخزين المؤقت لبروتوكول HTTP. نتيجةً لذلك، لا يتم أخذ توجيهات مثل Cache-Control: no-cache أو Cache-Control: max-age=0 في الاعتبار، ولا تتم إعادة التحقّق من الصحة قبل عرض المحتوى للمستخدم.

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

تعديل البيانات القديمة أو الحسّاسة بعد استعادة bfcache

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

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

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

لتجنُّب حالات كهذه، من المستحسن تعديل الصفحة دائمًا بعد حدث pageshow إذا كانت قيمة event.persisted هي true:

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Do any checks and updates to the page
  }
});

على الرغم من أنّ الأفضل هو تعديل المحتوى في مكانه، قد تحتاج إلى فرض إعادة تحميل كاملة لبعض التغييرات. يتحقّق الرمز التالي من توفّر ملف تعريف ارتباط خاص بالموقع الإلكتروني في الحدث pageshow، ويعيد تحميل الصفحة إذا لم يتم العثور على ملف تعريف الارتباط:

window.addEventListener('pageshow', (event) => {
  if (event.persisted && !document.cookie.match(/my-cookie)) {
    // Force a reload if the user has logged out.
    location.reload();
  }
});

تتميّز عملية إعادة التحميل بأنّها ستحتفظ بالسجلّ (للسماح بعمليات التنقّل للأمام)، ولكن قد تكون عملية إعادة التوجيه أكثر ملاءمة في بعض الحالات.

الإعلانات واستعادة الصفحات من ذاكرة التخزين المؤقت

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

بالنسبة إلى المواقع الإلكترونية التي تريد إعادة تحميل الإعلانات عند استعادة الصفحة من ذاكرة التخزين المؤقت الخلفية، تتيح إعادة تحميل الإعلانات فقط عند وقوع الحدث pageshow عندما تكون قيمة event.persisted هي true إمكانية إجراء ذلك بدون التأثير في أداء الصفحة. يُرجى التواصل مع مزوّد الإعلانات، ولكن إليك مثال على كيفية إجراء ذلك باستخدام "علامة ناشر Google".

تجنُّب الإشارات إلى window.opener

في المتصفحات القديمة، إذا تم فتح صفحة باستخدام window.open() من رابط يتضمّن target=_blank، بدون تحديد rel="noopener"، سيكون للصفحة التي تم فتحها مرجع إلى عنصر النافذة الخاص بالصفحة التي تم فتحها.

بالإضافة إلى كونها تشكّل خطرًا أمنيًا، لا يمكن وضع صفحة تتضمّن مرجعًا غير فارغ window.opener في ذاكرة التخزين المؤقت للخلف والأمام بأمان، لأنّ ذلك قد يؤدي إلى تعطيل أي صفحات تحاول الوصول إليها.

نتيجةً لذلك، من الأفضل تجنُّب إنشاء مراجع window.opener. يمكنك إجراء ذلك باستخدام rel="noopener" كلما أمكن ذلك (يُرجى العِلم أنّ هذا هو الإعداد التلقائي الآن في جميع المتصفّحات الحديثة). إذا كان موقعك الإلكتروني يتطلّب فتح نافذة والتحكّم فيها من خلال window.postMessage() أو الإشارة مباشرةً إلى عنصر النافذة، لن تكون النافذة المفتوحة ولا النافذة الأصلية مؤهّلتَين للاستفادة من ذاكرة التخزين المؤقت للخلف/الأمام.

إغلاق الاتصالات المفتوحة قبل أن ينتقل المستخدم إلى صفحة أخرى

كما ذكرنا سابقًا، عندما يتم الاحتفاظ بصفحة في ذاكرة التخزين المؤقت للخلف والأمام، يتم إيقاف جميع مهام JavaScript المُجدوَلة مؤقتًا واستئنافها عند إخراج الصفحة من ذاكرة التخزين المؤقت.

إذا كانت مهام JavaScript المُجدوَلة هذه لا تصل إلا إلى واجهات برمجة تطبيقات DOM أو واجهات برمجة تطبيقات أخرى مخصّصة للصفحة الحالية فقط، لن يؤدي إيقاف هذه المهام مؤقتًا عندما لا تكون الصفحة مرئية للمستخدم إلى حدوث أي مشاكل.

ومع ذلك، إذا كانت هذه المهام مرتبطة بواجهات برمجة تطبيقات يمكن الوصول إليها أيضًا من صفحات أخرى في المصدر نفسه (مثل IndexedDB وWeb Locks وWebSockets)، قد يؤدي إيقاف هذه المهام مؤقتًا إلى منع تشغيل الرمز في علامات تبويب أخرى.

نتيجةً لذلك، لن تحاول بعض المتصفّحات وضع صفحة في ذاكرة التخزين المؤقت للخلف والأمام في السيناريوهات التالية:

  • الصفحات التي تحتوي على اتصال IndexedDB مفتوح
  • الصفحات التي تتضمّن عمليات fetch() أو XMLHttpRequest قيد التقدّم
  • الصفحات التي تتضمّن اتصال WebSocket أو WebRTC مفتوحًا لا يحظر Chrome (الإصدار 149 والإصدارات الأحدث) وSafari فتح WebSockets، بينما تحظر المتصفّحات الأخرى ذلك.

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

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

يوضّح المثال التالي كيفية التأكّد من أنّ الصفحات التي تستخدم IndexedDB مؤهَّلة للاستفادة من ميزة "التخزين المؤقت للصفحات" من خلال إغلاق اتصال مفتوح في أداة معالجة الأحداث pagehide:

let dbPromise;
function openDB() {
  if (!dbPromise) {
    dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open('my-db', 1);
      req.onupgradeneeded = () => req.result.createObjectStore('keyval');
      req.onerror = () => reject(req.error);
      req.onsuccess = () => resolve(req.result);
    });
  }
  return dbPromise;
}

// Close the connection to the database when the user leaves.
window.addEventListener('pagehide', () => {
  if (dbPromise) {
    dbPromise.then(db => db.close());
    dbPromise = null;
  }
});

// Open the connection when the page is loaded or restored from bfcache.
window.addEventListener('pageshow', () => openDB());

اختبار للتأكّد من إمكانية تخزين صفحاتك مؤقتًا

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

لاختبار صفحة، اتّبِع الخطوات التالية:

  1. انتقِل إلى الصفحة في Chrome.
  2. في "أدوات مطوّري البرامج"، انتقِل إلى التطبيق -> ذاكرة التخزين المؤقت للصفحات السابقة واللاحقة.
  3. انقر على الزر تشغيل الاختبار. بعد ذلك، تحاول "أدوات مطوّري البرامج" الانتقال إلى صفحة أخرى ثم الرجوع إلى الصفحة الأصلية لتحديد ما إذا كان يمكن استعادة الصفحة من ذاكرة التخزين المؤقت للخلف والأمام.
لوحة "التخزين المؤقت للصفحات" في "أدوات مطوّري البرامج"
لوحة التخزين المؤقت للصفحات في "أدوات مطوّري البرامج"

في حال نجاح الاختبار، ستعرض اللوحة الرسالة "تمت الاستعادة من ميزة التخزين المؤقت للصفحات".

أدوات مطوّري البرامج تُبلغ عن استعادة صفحة بنجاح من ذاكرة التخزين المؤقت للخلف والأمام
صفحة تم استعادتها بنجاح

إذا لم تنجح العملية، ستوضّح اللوحة السبب. إذا كان السبب شيئًا يمكنك معالجته كمطوّر، ستضع اللوحة علامة قابل للتنفيذ عليه.

تعذُّر استعادة صفحة من ذاكرة التخزين المؤقت للصفحات في "أدوات مطوّري البرامج"
اختبار غير ناجح لميزة "التخزين المؤقت للصفحات" مع نتيجة قابلة للتنفيذ

في هذا المثال، يؤدي استخدام أداة معالجة الحدث unload إلى جعل الصفحة غير مؤهَّلة للاستفادة من ميزة "التخزين المؤقت للصفحات". يمكنك حلّ هذه المشكلة من خلال التبديل من unload إلى pagehide باتّباع الخطوات التالية:

الإجراءات الموصى بها
window.addEventListener('pagehide', ...);
الإجراءات غير المُوصى بها
window.addEventListener('unload', ...);

أضافت الإصدار 10.0 من Lighthouse أيضًا عملية تدقيق في ذاكرة التخزين المؤقت للخلف والأمام، والتي تجري اختبارًا مشابهًا. لمزيد من المعلومات، اطّلِع على مستندات تدقيق bfcache.

تأثير bfcache في قياس الأداء والإحصاءات

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

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

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

يوضّح المثال التالي كيفية إجراء ذلك باستخدام "إحصاءات Google". من المحتمل أنّ أدوات الإحصاءات الأخرى تستخدم منطقًا مشابهًا:

// Send a pageview when the page is first loaded.
// This happens by default just by loading gtag
gtag('config', 'TAG_ID');

window.addEventListener('pageshow', (event) => {
  // Send another pageview if the page is restored from bfcache.
  if (event.persisted) {
    gtag('event', 'page_view');
  }
});

قياس نسبة مرات التحقّق من ذاكرة التخزين المؤقت للخلف والأمام

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

// Send a navigation_type when the page is first loaded.
// To do this disable the default pageview so you can manually send it
// supplemented with the additional detail.
gtag('config', 'TAG_ID', { send_page_view: false });
gtag('event', 'page_view', {
   'navigation_type': performance.getEntriesByType('navigation')[0].type;
});

window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // Send another pageview if the page is restored from bfcache.
    gtag('event', 'page_view', {
      'navigation_type': 'back_forward_cache';
    });
  }
});

يمكنك احتساب نسبة نجاح التخزين المؤقت للصفحات باستخدام عدد عمليات التنقّل back_forward وعمليات التنقّل back_forward_cache.

من المهمّ إدراك أنّ هناك عددًا من السيناريوهات التي لا يمكن لمالكي المواقع الإلكترونية التحكّم بها، ولن يتم فيها استخدام ذاكرة التخزين المؤقت للخلف/الأمام، بما في ذلك:

  • عندما يغلق المستخدم المتصفّح ويعيد فتحه
  • عندما يكرّر المستخدم علامة تبويب
  • عندما يغلق المستخدم علامة تبويب ثم يعيد فتحها

في بعض هذه الحالات، قد تحتفظ بعض المتصفّحات بنوع التنقّل الأصلي، وبالتالي قد تعرض نوعًا من back_forward على الرغم من أنّها ليست عمليات تنقّل للخلف أو للأمام.

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

لذلك، لا ينبغي أن يتوقّع مالكو المواقع الإلكترونية تحقيق نسبة نجاح تبلغ% 100 في استخدام ذاكرة التخزين المؤقت للخلف والأمام لجميع عمليات التنقّل back_forward. ومع ذلك، يمكن أن يكون قياس النسبة مفيدًا لتحديد الصفحات التي تمنع استخدام ميزة "التخزين المؤقت للصفحات" لنسبة كبيرة من عمليات التنقّل إلى الخلف وإلى الأمام.

أضاف فريق Chrome واجهة برمجة التطبيقات NotRestoredReasons للمساعدة في توضيح أسباب عدم استخدام الصفحات لذاكرة التخزين المؤقت للخلف والأمام، ما يتيح للمطوّرين تحسين معدّلات الاستفادة من ذاكرة التخزين المؤقت للخلف والأمام. أضاف فريق Chrome أيضًا أنواع التنقّل إلى CrUX، ما يتيح الاطّلاع على عدد عمليات التنقّل من خلال ذاكرة التخزين المؤقت للصفحة الخلفية حتى بدون قياسها بنفسك.

قياس الأداء

يمكن أن يؤثر bfcache أيضًا سلبًا في مقاييس الأداء التي يتم جمعها في الحقل، وتحديدًا المقاييس التي تقيس أوقات تحميل الصفحة.

بما أنّ عمليات التنقّل باستخدام التخزين المؤقت للصفحات تستعيد صفحة حالية بدلاً من بدء تحميل صفحة جديدة، سينخفض إجمالي عدد عمليات تحميل الصفحات التي يتم جمعها عند تفعيل ميزة التخزين المؤقت للصفحات. مع ذلك، من المهم ملاحظة أنّ عمليات تحميل الصفحات التي تم استبدالها بعمليات استعادة من ذاكرة التخزين المؤقت للخلف والأمام كانت على الأرجح من بين عمليات تحميل الصفحات الأسرع في مجموعة البيانات. يرجع ذلك إلى أنّ عمليات التنقّل بين الصفحات السابقة واللاحقة، بحسب التعريف، هي زيارات متكرّرة، وعمليات تحميل الصفحات المتكرّرة تكون بشكل عام أسرع من عمليات تحميل الصفحات من الزوّار لأول مرة (بسبب التخزين المؤقت لبروتوكول نقل النص التشعبي (HTTP)، كما ذكرنا سابقًا).

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

هناك بضع طرق للتعامل مع هذه المشكلة. إحدى الطرق هي إضافة تعليقات توضيحية إلى جميع مقاييس تحميل الصفحة مع نوع التنقّل الخاص بكل منها: navigate أو reload أو back_forward أو prerender. يتيح لك ذلك مواصلة مراقبة أدائك ضمن أنواع التنقّل هذه، حتى إذا كان التوزيع العام يميل إلى السلبية. ننصح بهذا الأسلوب لمقاييس تحميل الصفحات غير المرتبطة بالمستخدمين، مثل الوقت اللازم لظهور أول بايت (TTFB).

بالنسبة إلى المقاييس التي تركّز على المستخدم، مثل Core Web Vitals، من الأفضل عرض قيمة تمثّل بشكل أكثر دقة تجربة المستخدم.

التأثير في Core Web Vitals

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

تتعامل الأدوات التي تجمع بيانات مقاييس Core Web Vitals وتقدّم تقارير عنها، مثل تقرير تجربة المستخدم في Chrome، مع عمليات استعادة الصفحات من ذاكرة التخزين المؤقت للصفحات على أنّها زيارات منفصلة للصفحة في مجموعة البيانات. على الرغم من عدم توفّر واجهات برمجة تطبيقات مخصّصة لأداء الويب لقياس هذه المقاييس بعد عمليات الاستعادة من ذاكرة التخزين المؤقت للخلف والأمام، يمكنك تقريب قيمها باستخدام واجهات برمجة تطبيقات الويب الحالية:

  • بالنسبة إلى سرعة عرض أكبر محتوى مرئي (LCP)، استخدِم الفرق بين الطابع الزمني لحدث pageshow والطابع الزمني للإطار التالي الذي تم رسمه، لأنّه سيتم رسم جميع العناصر في الإطار في الوقت نفسه. في حال استعادة الصفحة من ذاكرة التخزين المؤقت للخلف والأمام، يكون كلّ من مقياسَي LCP وFCP متطابقَين.
  • بالنسبة إلى مدى استجابة الصفحة لتفاعلات المستخدم (INP)، استمرّ في استخدام Performance Observer الحالي، ولكن أعِد ضبط قيمة INP الحالية على 0.
  • بالنسبة إلى متغيّرات التصميم التراكمية (CLS)، استمر في استخدام Performance Observer الحالي، ولكن أعِد ضبط قيمة CLS الحالية على 0.

للحصول على مزيد من التفاصيل حول تأثير bfcache في كل مقياس، اطّلِع على صفحات أدلة المقاييس الفردية لمؤشرات Core Web Vitals. للحصول على مثال محدّد حول كيفية تنفيذ إصدارات bfcache من هذه المقاييس، يُرجى الرجوع إلى طلب السحب الذي يضيفها إلى مكتبة JavaScript الخاصة بمقاييس Web Vitals.

تتيح مكتبة JavaScript web-vitals عمليات الاستعادة من ذاكرة التخزين المؤقت للخلف في المقاييس التي تعرضها.

موارد إضافية