کش عقب / جلو

حافظه پنهان Back/Forward (یا bfcache) یک بهینه‌سازی مرورگر است که امکان پیمایش سریع به عقب و جلو را فراهم می‌کند. این قابلیت به طور قابل توجهی تجربه مرور وب را بهبود می‌بخشد، به خصوص برای کاربرانی که شبکه‌ها یا دستگاه‌های کندتری دارند.

به عنوان توسعه‌دهندگان وب، بسیار مهم است که بدانید چگونه صفحات خود را برای bfcache بهینه کنید تا کاربران شما بتوانند از مزایای آن بهره‌مند شوند.

سازگاری با مرورگرها

همه مرورگرهای اصلی شامل bfcache هستند، از جمله کروم از نسخه ۹۶ به بعد، فایرفاکس و سافاری .

اصول اولیه bfcache

با استفاده از حافظه پنهان back/forward (bfcache)، به جای اینکه هنگام خروج کاربر از صفحه، آن را از بین ببریم، تخریب را به تعویق می‌اندازیم و اجرای JS را متوقف می‌کنیم. اگر کاربر به زودی به عقب برگردد، صفحه را دوباره قابل مشاهده می‌کنیم و اجرای JS را از حالت مکث خارج می‌کنیم. این منجر به پیمایش تقریباً فوری صفحه برای کاربر می‌شود.

چند بار شده که از یک وب‌سایت بازدید کرده‌اید و روی لینکی کلیک کرده‌اید تا به صفحه دیگری بروید، اما متوجه شده‌اید که این صفحه آن چیزی نیست که می‌خواستید و دکمه بازگشت را زده‌اید؟ در آن لحظه، bfcache می‌تواند تفاوت زیادی در سرعت بارگذاری صفحه قبلی ایجاد کند:

بدون فعال بودن bfcache یک درخواست جدید برای بارگذاری صفحه قبلی آغاز می‌شود و بسته به اینکه آن صفحه چقدر برای بازدیدهای مکرر بهینه شده است، مرورگر ممکن است مجبور شود برخی (یا همه) منابعی را که تازه دانلود کرده است، دوباره دانلود، تجزیه و اجرا کند.
با فعال بودن bfcache بارگذاری صفحه قبلی اساساً فوری است، زیرا کل صفحه را می‌توان از حافظه بازیابی کرد، بدون اینکه اصلاً نیازی به مراجعه به شبکه باشد.

برای درک سرعتی که bfcache می‌تواند برای ناوبری به ارمغان بیاورد، این ویدیو از نحوه‌ی عملکرد آن را تماشا کنید:

استفاده از bfcache باعث می‌شود صفحات در هنگام پیمایش به عقب و جلو بسیار سریع‌تر بارگذاری شوند.

در ویدیو، مثالی که از bfcache استفاده می‌کند، بسیار سریع‌تر از مثالی است که از آن استفاده نمی‌کند.

bfcache نه تنها سرعت پیمایش را افزایش می‌دهد، بلکه استفاده از داده را نیز کاهش می‌دهد، زیرا منابع نیازی به دانلود مجدد ندارند.

داده‌های استفاده از کروم نشان می‌دهد که از هر ۱۰ پیمایش در دسکتاپ، ۱ پیمایش و در موبایل، ۱ پیمایش یا به عقب یا به جلو است. با فعال بودن bfcache، مرورگرها می‌توانند انتقال داده‌ها و زمان صرف شده برای بارگذاری میلیاردها صفحه وب را در هر روز از بین ببرند!

نحوه کار «حافظه پنهان»

«حافظه پنهان» مورد استفاده توسط bfcache با حافظه پنهان HTTP متفاوت است، که نقش خود را در سرعت بخشیدن به پیمایش‌های تکراری ایفا می‌کند. bfcache یک تصویر لحظه‌ای از کل صفحه در حافظه، از جمله پشته جاوا اسکریپت است، در حالی که حافظه پنهان HTTP فقط شامل پاسخ‌های درخواست‌های قبلی است. از آنجایی که بسیار نادر است که تمام درخواست‌های مورد نیاز برای بارگذاری یک صفحه از حافظه پنهان HTTP انجام شوند، بازدیدهای مکرر با استفاده از بازیابی‌های bfcache همیشه سریع‌تر از حتی بهینه‌ترین پیمایش‌های غیر bfcache هستند.

فریز کردن یک صفحه برای فعال‌سازی مجدد آن در آینده، از نظر بهترین روش برای حفظ کد در حال انجام، پیچیدگی‌هایی را به همراه دارد. برای مثال، چگونه فراخوانی‌های setTimeout() را در جایی که timeout به پایان می‌رسد در حالی که صفحه در bfcache است، مدیریت می‌کنید؟

پاسخ این است که مرورگرها هرگونه تایمر در حال انتظار یا promise های حل نشده برای صفحات موجود در bfcache، از جمله تقریباً تمام وظایف در حال انتظار در صف وظایف جاوا اسکریپت را متوقف می‌کنند و در صورت بازیابی صفحه از bfcache، پردازش وظایف را از سر می‌گیرند.

در برخی موارد، مانند زمان‌های انتظار و وعده‌ها، این ریسک نسبتاً پایینی دارد، اما در موارد دیگر می‌تواند منجر به رفتار گیج‌کننده یا غیرمنتظره شود. به عنوان مثال، اگر مرورگر وظیفه‌ای را که به عنوان بخشی از یک تراکنش IndexedDB مورد نیاز است، متوقف کند، می‌تواند بر سایر تب‌های باز در همان مبدا تأثیر بگذارد، زیرا چندین تب می‌توانند به طور همزمان به همان پایگاه‌های داده IndexedDB دسترسی داشته باشند. در نتیجه، مرورگرها معمولاً سعی نمی‌کنند صفحات را در وسط یک تراکنش IndexedDB یا هنگام استفاده از APIهایی که ممکن است بر صفحات دیگر تأثیر بگذارند، ذخیره کنند.

برای جزئیات بیشتر در مورد چگونگی تأثیر استفاده از APIهای مختلف بر واجد شرایط بودن یک صفحه برای bfcache، به بخش «صفحات خود را برای bfcache بهینه کنید» مراجعه کنید.

bfcache و iframe ها

اگر صفحه‌ای حاوی iframe های جاسازی‌شده باشد، خود iframe ها به طور جداگانه واجد شرایط bfcache نیستند. برای مثال، اگر در یک iframe به URL دیگری بروید، محتوای قبلی وارد bfcache نمی‌شود و اگر به عقب برگردید، مرورگر به جای فریم اصلی، در iframe به عقب برمی‌گردد، اما پیمایش به عقب در iframe از bfcache استفاده نمی‌کند.

با این حال، هنگامی که فریم اصلی از bfcache بازیابی می‌شود، iframe های جاسازی شده به همان شکلی که هنگام ورود صفحه به bfcache بودند، بازیابی می‌شوند.

اگر یک iframe تعبیه‌شده از APIهایی استفاده کند که این قابلیت را مسدود می‌کنند، می‌توان دسترسی فریم اصلی به bfcache را نیز مسدود کرد. برای جلوگیری از این امر می‌توان از سیاست مجوزها (Permissions Policy) تنظیم‌شده روی فریم اصلی یا استفاده از ویژگی‌های sandbox استفاده کرد.

bfcache و برنامه‌های تک صفحه‌ای (SPA)

از آنجایی که bfcache با ناوبری‌های مدیریت‌شده توسط مرورگر کار می‌کند، برای «ناوبری‌های نرم» در یک برنامه تک‌صفحه‌ای (SPA) کار نمی‌کند. با این حال، bfcache همچنان می‌تواند هنگام بازگشت به یک SPA به جای راه‌اندازی مجدد کامل آن برنامه از ابتدا، مفید باشد.

APIهایی برای مشاهده‌ی bfcache

اگرچه bfcache بهینه‌سازی‌ای است که مرورگرها به طور خودکار انجام می‌دهند، اما هنوز هم برای توسعه‌دهندگان مهم است که بدانند چه زمانی این اتفاق می‌افتد تا بتوانند صفحات خود را برای آن بهینه کنند و هرگونه معیار یا اندازه‌گیری عملکرد را بر اساس آن تنظیم کنند .

رویدادهای اصلی مورد استفاده برای مشاهده bfcache، رویدادهای انتقال صفحه pageshow و pagehide هستند که توسط اکثر مرورگرها پشتیبانی می‌شوند.

رویدادهای جدیدتر چرخه عمر صفحه - freeze و resume - نیز هنگام ورود یا خروج صفحات به bfcache و همچنین در برخی موقعیت‌های دیگر، به عنوان مثال، هنگامی که یک تب پس‌زمینه برای به حداقل رساندن استفاده از CPU ثابت می‌شود، ارسال می‌شوند. این رویدادها فقط در مرورگرهای مبتنی بر Chromium پشتیبانی می‌شوند.

مشاهده کنید که چه زمانی یک صفحه از bfcache بازیابی می‌شود

رویداد pageshow درست پس از رویداد load ، زمانی که صفحه برای اولین بار بارگذاری می‌شود و هر زمان که صفحه از bfcache بازیابی شود، اجرا می‌شود. رویداد pageshow دارای یک ویژگی persisted است که اگر صفحه از bfcache بازیابی شده باشد، مقدار آن true و در غیر این صورت false است. می‌توانید از ویژگی persisted برای تشخیص بارگذاری‌های منظم صفحه از بازیابی‌های bfcache استفاده کنید. برای مثال:

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

در مرورگرهایی که از API چرخه عمر صفحه پشتیبانی می‌کنند، رویداد resume زمانی فعال می‌شود که صفحات از bfcache بازیابی شوند (بلافاصله قبل از رویداد pageshow ) و زمانی که کاربر دوباره از یک تب پس‌زمینه‌ی مسدود شده بازدید می‌کند. اگر می‌خواهید وضعیت یک صفحه را پس از مسدود شدن به‌روزرسانی کنید (که شامل صفحات موجود در bfcache نیز می‌شود)، می‌توانید از رویداد resume استفاده کنید، اما اگر می‌خواهید میزان بازدید سایت خود از bfcache را اندازه‌گیری کنید، باید از رویداد pageshow استفاده کنید. در برخی موارد، ممکن است لازم باشد از هر دو استفاده کنید.

برای جزئیات بیشتر در مورد بهترین شیوه‌های اندازه‌گیری bfcache، به «چگونه bfcache بر تجزیه و تحلیل و اندازه‌گیری عملکرد تأثیر می‌گذارد» مراجعه کنید.

مشاهده‌ی زمان ورود یک صفحه به bfcache

رویداد pagehide یا زمانی که یک صفحه خالی می‌شود یا زمانی که مرورگر سعی می‌کند آن را در bfcache قرار دهد، فعال می‌شود.

رویداد pagehide همچنین دارای یک ویژگی persisted است. اگر مقدار آن false ، می‌توانید مطمئن باشید که آن صفحه قرار نیست وارد bfcache شود. با این حال، اگر persisted true باشد، تضمین نمی‌کند که یک صفحه cache شود. این بدان معناست که مرورگر قصد دارد صفحه را cache کند، اما ممکن است عوامل دیگری وجود داشته باشد که cache کردن آن را غیرممکن کند.

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 ذخیره نمی‌شوند، و حتی وقتی صفحه‌ای در آنجا ذخیره می‌شود، به طور نامحدود در آنجا باقی نمی‌ماند. بسیار مهم است که توسعه‌دهندگان بدانند چه چیزی صفحات را برای bfcache واجد شرایط (و غیر واجد شرایط) می‌کند تا نرخ موفقیت حافظه پنهان خود را به حداکثر برسانند.

بخش‌های زیر بهترین شیوه‌ها را برای افزایش احتمال ذخیره صفحات شما در حافظه پنهان مرورگر ارائه می‌دهند.

هرگز از رویداد unload استفاده نکنید

مهم‌ترین راه برای بهینه‌سازی bfcache در همه مرورگرها این است که هرگز از رویداد unload استفاده نکنید. هرگز!

رویداد unload برای مرورگرها مشکل‌ساز است زیرا قبل از bfcache وجود داشته و بسیاری از صفحات در اینترنت با این فرض (معقول) کار می‌کنند که یک صفحه پس از اجرای رویداد unload دیگر وجود نخواهد داشت. این یک چالش است زیرا بسیاری از آن صفحات نیز با این فرض ساخته شده‌اند که رویداد unload هر زمان که کاربر در حال حرکت به خارج از صفحه باشد، اجرا می‌شود، که دیگر درست نیست (و مدت زیادی است که درست نبوده است ).

بنابراین مرورگرها با یک دوراهی روبرو هستند، آنها باید بین چیزی که می‌تواند تجربه کاربری را بهبود بخشد - اما ممکن است خطر خرابی صفحه را نیز به همراه داشته باشد - یکی را انتخاب کنند.

در دسکتاپ، کروم و فایرفاکس تصمیم گرفته‌اند صفحاتی را که یک شنونده‌ی unload اضافه می‌کنند، برای bfcache غیرقابل استفاده کنند، که ریسک کمتری دارد اما بسیاری از صفحات را نیز رد صلاحیت می‌کند. سافاری سعی می‌کند برخی صفحات را با یک شنونده‌ی رویداد unload ذخیره کند، اما برای کاهش احتمال خرابی، رویداد unload را هنگام حرکت کاربر اجرا نمی‌کند، که این امر باعث می‌شود این رویداد بسیار غیرقابل اعتماد باشد.

در موبایل، کروم و سافاری سعی می‌کنند صفحات را با یک شنونده رویداد unload ذخیره کنند، زیرا خطر خرابی به دلیل این واقعیت که رویداد unload همیشه در موبایل بسیار غیرقابل اعتماد بوده است، کمتر است. فایرفاکس صفحاتی را که unload استفاده می‌کنند، برای bfcache غیرقابل قبول می‌داند، به جز در iOS که به همه مرورگرها نیاز دارد از موتور رندر WebKit استفاده کنند و بنابراین مانند سافاری رفتار می‌کند.

به جای استفاده از رویداد unload ، از رویداد pagehide استفاده کنید. رویداد pagehide در تمام مواردی که رویداد unload اجرا می‌شود، و همچنین هنگامی که صفحه‌ای در bfcache قرار می‌گیرد، اجرا می‌شود.

در واقع، Lighthouse یک حسابرسی no-unload-listeners دارد که در صورت وجود هرگونه جاوا اسکریپت در صفحاتشان (از جمله کتابخانه‌های شخص ثالث) که یک شنونده رویداد unload اضافه می‌کند، به توسعه‌دهندگان هشدار می‌دهد.

به دلیل غیرقابل اعتماد بودن و تأثیر آن بر عملکرد bfcache، کروم در حال بررسی منسوخ کردن رویداد unload است.

از سیاست مجوز برای جلوگیری از استفاده از unload handlerها در یک صفحه استفاده کنید.

سایت‌هایی که unload event handler استفاده نمی‌کنند، می‌توانند با استفاده از Permissions Policy از عدم اضافه شدن این موارد اطمینان حاصل کنند.

Permissions-Policy: unload=()

این همچنین مانع از آن می‌شود که اشخاص ثالث یا افزونه‌ها با اضافه کردن unload handlers و غیرقابل قبول کردن سایت برای bfcache، سرعت سایت را کاهش دهند.

فقط شنونده‌های beforeunload را به صورت مشروط اضافه کنید

رویداد beforeunload صفحات شما را در مرورگرهای مدرن از bfcache محروم نمی‌کند، اما قبلاً این کار را می‌کرد و هنوز هم غیرقابل اعتماد است، بنابراین از استفاده از آن خودداری کنید، مگر اینکه کاملاً ضروری باشد.

با این حال، برخلاف رویداد 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 ذخیره نکند. این برای منابعی که حاوی اطلاعات حساس کاربر هستند، مانند صفحات پشت صفحه ورود، استفاده می‌شود.

اگرچه bfcache یک حافظه پنهان HTTP نیست، اما از نظر تاریخی، وقتی Cache-Control: no-store روی خود منبع صفحه تنظیم می‌شود (برخلاف هر زیرمنبعی)، مرورگرها تصمیم گرفته‌اند که صفحه را در bfcache ذخیره نکنند، بنابراین هر صفحه‌ای که از Cache-Control: no-store استفاده می‌کند، ممکن است واجد شرایط bfcache نباشد. کارهایی در حال انجام است تا این رفتار برای کروم به شیوه‌ای حفظ حریم خصوصی تغییر کند .

از آنجایی که Cache-Control: no-store واجد شرایط بودن یک صفحه برای bfcache را محدود می‌کند، فقط باید روی صفحاتی تنظیم شود که حاوی اطلاعات حساس هستند و هیچ نوع ذخیره‌سازی در حافظه پنهان (caching) در آنها هرگز مناسب نیست.

برای صفحاتی که باید همیشه محتوای به‌روز ارائه دهند - و آن محتوا حاوی اطلاعات حساس نیست - Cache-Control: no-cache یا Cache-Control: max-age=0 استفاده کنید. این دستورالعمل‌ها به مرورگر دستور می‌دهند که قبل از ارائه محتوا، آن را مجدداً اعتبارسنجی کند و بر واجد شرایط بودن bfcache یک صفحه تأثیری ندارند.

توجه داشته باشید که وقتی صفحه‌ای از bfcache بازیابی می‌شود، از حافظه بازیابی می‌شود، نه از حافظه پنهان HTTP. در نتیجه، دستورالعمل‌هایی مانند Cache-Control: no-cache یا Cache-Control: max-age=0 در نظر گرفته نمی‌شوند و قبل از نمایش محتوا به کاربر، هیچ اعتبارسنجی مجددی انجام نمی‌شود.

با این حال، این احتمالاً هنوز هم تجربه کاربری بهتری خواهد داشت، زیرا بازیابی‌های bfcache فوری هستند و - از آنجایی که صفحات برای مدت طولانی در bfcache نمی‌مانند - بعید است که محتوا قدیمی باشد. با این حال، اگر محتوای شما دقیقه به دقیقه تغییر می‌کند، می‌توانید با استفاده از رویداد pageshow ، همانطور که در بخش بعدی توضیح داده شده است، هرگونه به‌روزرسانی را دریافت کنید.

به‌روزرسانی داده‌های قدیمی یا حساس پس از بازیابی bfcache

اگر سایت شما وضعیت کاربر - به خصوص هرگونه اطلاعات حساس کاربر - را نگه می‌دارد، پس از بازیابی صفحه از bfcache، این داده‌ها باید به‌روزرسانی یا پاک شوند.

برای مثال، اگر کاربری به صفحه پرداخت برود و سپس سبد خرید خود را به‌روزرسانی کند، اگر یک صفحه قدیمی از 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();
  }
});

بارگذاری مجدد این مزیت را دارد که همچنان تاریخچه را حفظ می‌کند (تا امکان پیمایش به جلو فراهم شود)، اما در برخی موارد، تغییر مسیر ممکن است مناسب‌تر باشد.

بازیابی تبلیغات و bfcache

ممکن است وسوسه‌انگیز باشد که سعی کنید از استفاده از bfcache برای نمایش مجموعه‌ای جدید از تبلیغات در هر پیمایش به عقب/جلو خودداری کنید. با این حال، علاوه بر تأثیر بر عملکرد، جای سوال است که آیا چنین رفتاری منجر به تعامل بهتر با تبلیغات می‌شود یا خیر. کاربران ممکن است متوجه تبلیغی شده باشند که قصد بازگشت به آن و کلیک کردن روی آن را داشته باشند، اما با بارگذاری مجدد به جای بازیابی از bfcache، قادر به انجام این کار نباشند. آزمایش این سناریو - در حالت ایده‌آل با یک تست A/B - قبل از هرگونه فرضیه‌سازی مهم است.

برای سایت‌هایی که می‌خواهند تبلیغات را در bfcache restore به‌روزرسانی کنند، به‌روزرسانی فقط تبلیغات در رویداد pageshow زمانی که event.persisted روی true تنظیم شده باشد، اجازه می‌دهد این اتفاق بدون تأثیر بر عملکرد صفحه رخ دهد. با ارائه‌دهنده تبلیغات خود مشورت کنید، اما در اینجا یک مثال در مورد نحوه انجام این کار با Google Publishing Tag آورده شده است .

از ارجاع window.opener خودداری کنید

در مرورگرهای قدیمی‌تر، اگر صفحه‌ای با استفاده از تابع window.open() از لینکی با target=_blank و بدون مشخص کردن rel="noopener" باز می‌شد، صفحه‌ی باز شده دارای ارجاعی به شیء window صفحه‌ی باز شده می‌بود.

علاوه بر اینکه یک ریسک امنیتی است ، صفحه‌ای با ارجاع غیر تهی window.opener را نمی‌توان با خیال راحت در bfcache قرار داد، زیرا این کار می‌تواند هر صفحه‌ای را که سعی در دسترسی به آن دارد، از کار بیندازد.

در نتیجه، بهتر است از ایجاد ارجاعات window.opener خودداری کنید. می‌توانید این کار را با استفاده از rel="noopener" در هر زمان ممکن انجام دهید (توجه داشته باشید، این اکنون به طور پیش‌فرض در همه مرورگرهای مدرن است). اگر سایت شما نیاز به باز کردن یک پنجره و کنترل آن از طریق window.postMessage() یا ارجاع مستقیم به شیء window دارد، نه پنجره باز شده و نه بازکننده واجد شرایط دریافت bfcache نخواهند بود.

قبل از اینکه کاربر از صفحه خارج شود، اتصالات باز را ببندید

همانطور که قبلاً ذکر شد، وقتی صفحه‌ای در bfcache نگهداری می‌شود، تمام وظایف جاوا اسکریپت زمان‌بندی‌شده را متوقف می‌کند و وقتی صفحه از حافظه پنهان خارج شود، آنها را از سر می‌گیرد.

اگر این وظایف زمان‌بندی‌شده‌ی جاوااسکریپت فقط به APIهای DOM یا سایر APIهای مجزا از صفحه‌ی فعلی دسترسی داشته باشند، متوقف کردن این وظایف در حالی که صفحه برای کاربر قابل مشاهده نیست، مشکلی ایجاد نمی‌کند.

با این حال، اگر این وظایف به APIهایی متصل باشند که از صفحات دیگر در همان مبدا نیز قابل دسترسی هستند (برای مثال: IndexedDB، Web Locks، WebSockets)، این می‌تواند مشکل‌ساز باشد زیرا متوقف کردن این وظایف ممکن است از اجرای کد در تب‌های دیگر جلوگیری کند.

در نتیجه، برخی از مرورگرها در سناریوهای زیر سعی در قرار دادن صفحه در bfcache نمی‌کنند:

اگر صفحه شما از هر یک از این APIها استفاده می‌کند، اکیداً توصیه می‌کنیم در طول رویداد pagehide یا freeze ، اتصالات را ببندید و مشاهده‌گرها را حذف یا قطع کنید. این کار به مرورگر اجازه می‌دهد تا بدون خطر تأثیرگذاری بر سایر تب‌های باز، صفحه را با خیال راحت ذخیره کند.

سپس، اگر صفحه از bfcache بازیابی شود، می‌توانید در طول رویداد pageshow یا resume ، آن APIها را دوباره باز کنید یا دوباره به آنها متصل شوید.

مثال زیر نشان می‌دهد که چگونه می‌توان با بستن یک اتصال باز در شنونده رویداد pagehide از واجد شرایط بودن صفحاتی که از IndexedDB استفاده می‌کنند برای bfcache اطمینان حاصل کرد:

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 DevTools) می‌توانند به شما کمک کنند صفحات خود را آزمایش کنید تا از بهینه‌سازی آنها برای bfcache اطمینان حاصل کنید و هرگونه مشکلی را که ممکن است مانع از واجد شرایط بودن آنها شود، شناسایی کنید.

برای آزمایش یک صفحه:

  1. در کروم به صفحه مورد نظر بروید.
  2. در DevTools، به Application -> Back-forward Cache بروید.
  3. روی دکمه‌ی اجرای تست کلیک کنید. سپس DevTools سعی می‌کند به عقب و جلو حرکت کند تا مشخص کند که آیا صفحه می‌تواند از bfcache بازیابی شود یا خیر.
پنل حافظه پنهان رو به جلو در DevTools
پنل Back-forward Cache در DevTools.

اگر آزمایش موفقیت‌آمیز باشد، پنل عبارت «بازیابی از حافظه پنهان رو به جلو» را گزارش می‌دهد.

DevTools گزارش می‌دهد که یک صفحه با موفقیت از bfcache بازیابی شده است
صفحه با موفقیت بازیابی شد.

اگر ناموفق باشد، پنل دلیل آن را نشان می‌دهد. اگر دلیل چیزی باشد که شما به عنوان یک توسعه‌دهنده می‌توانید به آن رسیدگی کنید، پنل آن را به عنوان «قابل اقدام» علامت‌گذاری می‌کند.

گزارش DevTools مبنی بر عدم موفقیت در بازیابی صفحه از bfcache
یک تست bfcache ناموفق با نتیجه‌ای قابل پیگیری.

در این مثال، استفاده از شنونده رویداد unload باعث می‌شود صفحه برای bfcache واجد شرایط نباشد . می‌توانید با تغییر از unload به استفاده از pagehide این مشکل را برطرف کنید:

انجام دهید
window.addEventListener('pagehide', ...);
نکن
window.addEventListener('unload', ...);

Lighthouse 10.0 همچنین یک bfcache audit اضافه کرده است که آزمایش مشابهی را انجام می‌دهد. برای اطلاعات بیشتر، به مستندات bfcache audit مراجعه کنید.

چگونه bfcache بر تجزیه و تحلیل و اندازه‌گیری عملکرد تأثیر می‌گذارد

اگر از یک ابزار تحلیلی برای اندازه‌گیری بازدیدهای سایت خود استفاده می‌کنید، ممکن است متوجه کاهش تعداد کل بازدیدهای صفحه گزارش شده شوید، زیرا کروم bfcache را برای کاربران بیشتری فعال می‌کند.

در واقع، شما احتمالاً همین الان هم تعداد بازدیدهای صفحه را از مرورگرهای دیگری که bfcache را پیاده‌سازی کرده‌اند، کمتر از مقدار واقعی گزارش می‌کنید، زیرا بسیاری از کتابخانه‌های تحلیلی محبوب، بازیابی‌های bfcache را به عنوان بازدیدهای جدید صفحه اندازه‌گیری نمی‌کنند.

برای اینکه بازیابی‌های bfcache در تعداد بازدیدهای صفحه شما لحاظ شوند، برای رویداد pageshow شنونده تنظیم کنید و ویژگی persisted را بررسی کنید.

مثال زیر نحوه انجام این کار را با گوگل آنالیتیکس نشان می‌دهد. سایر ابزارهای تحلیلی احتمالاً از منطق مشابهی استفاده می‌کنند:

// Send a pageview when the page is first loaded.
gtag('event', 'page_view');

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

نسبت موفقیت bfcache خود را اندازه‌گیری کنید

همچنین می‌توانید بررسی کنید که آیا از bfcache استفاده شده است یا خیر، تا صفحاتی که از bfcache استفاده نمی‌کنند را شناسایی کنید. این کار را می‌توان با اندازه‌گیری نوع ناوبری برای بارگذاری صفحات انجام داد:

// Send a navigation_type when the page is first loaded.
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 ، نسبت موفقیت bfcache خود را محاسبه کنید.

مهم است بدانید که تعدادی سناریو، خارج از کنترل صاحبان سایت، وجود دارد که در آن‌ها ناوبری Back/Forward از bfcache استفاده نمی‌کند، از جمله:

  • وقتی کاربر از مرورگر خارج می‌شود و دوباره آن را اجرا می‌کند
  • وقتی کاربر یک تب را کپی می‌کند
  • وقتی کاربر یک تب را می‌بندد و دوباره باز می‌کند

در برخی از این موارد، نوع ناوبری اصلی ممکن است توسط برخی مرورگرها حفظ شود و بنابراین ممکن است نوعی از back_forward را نشان دهد، علیرغم اینکه این ناوبری‌ها Back/Forward نیستند.

حتی بدون این استثنائات، bfcache پس از مدتی برای صرفه‌جویی در حافظه حذف خواهد شد.

بنابراین، صاحبان وب‌سایت نباید انتظار داشته باشند که نسبت موفقیت bfcache برای همه ناوبری‌های back_forward صد در صد باشد. با این حال، اندازه‌گیری این نسبت می‌تواند برای شناسایی صفحاتی که خود صفحه مانع از استفاده از bfcache برای بخش زیادی از ناوبری‌های back و forward می‌شود، مفید باشد.

تیم کروم API مربوط به NotRestoredReasons را اضافه کرده است تا به آشکار شدن دلایل عدم استفاده صفحات از bfcache کمک کند، بنابراین توسعه‌دهندگان می‌توانند نرخ موفقیت bfcache خود را بهبود بخشند. تیم کروم همچنین انواع ناوبری را به CrUX اضافه کرده است که امکان مشاهده تعداد ناوبری‌های bfcache را حتی بدون اندازه‌گیری شخصی آن فراهم می‌کند.

اندازه‌گیری عملکرد

bfcache همچنین می‌تواند بر معیارهای عملکرد جمع‌آوری‌شده در این زمینه ، به‌ویژه معیارهایی که زمان بارگذاری صفحه را اندازه‌گیری می‌کنند، تأثیر منفی بگذارد.

از آنجایی که ناوبری‌های bfcache به جای شروع بارگذاری یک صفحه جدید، یک صفحه موجود را بازیابی می‌کنند، تعداد کل بارگذاری‌های صفحه جمع‌آوری‌شده با فعال شدن bfcache کاهش می‌یابد. با این حال، نکته مهم این است که بارگذاری‌های صفحه‌ای که با بازیابی‌های bfcache جایگزین می‌شوند، احتمالاً جزو سریع‌ترین بارگذاری‌های صفحه در مجموعه داده‌های شما بوده‌اند. دلیل این امر این است که ناوبری‌های عقب و جلو، طبق تعریف، بازدیدهای مکرر هستند و بارگذاری‌های مکرر صفحه معمولاً سریع‌تر از بارگذاری صفحه از بازدیدکنندگان بار اول هستند (به دلیل ذخیره‌سازی HTTP ، همانطور که قبلاً ذکر شد).

نتیجه، کاهش سرعت بارگذاری صفحات در مجموعه داده‌های شما است که احتمالاً توزیع را کندتر می‌کند - با وجود این واقعیت که عملکرد تجربه شده توسط کاربر احتمالاً بهبود یافته است!

چند راه برای مقابله با این مشکل وجود دارد. یکی از آنها حاشیه‌نویسی تمام معیارهای بارگذاری صفحه با نوع ناوبری مربوطه‌شان است: navigate ، reload ، back_forward یا prerender . این به شما امکان می‌دهد عملکرد خود را در این نوع ناوبری‌ها، حتی اگر توزیع کلی منفی باشد، همچنان رصد کنید. ما این رویکرد را برای معیارهای بارگذاری صفحه غیر کاربر محور مانند زمان اولین بایت (TTFB) توصیه می‌کنیم.

برای معیارهای کاربر محور مانند Core Web Vitals ، گزینه بهتر این است که مقداری را گزارش دهید که دقیق‌تر نشان دهنده تجربه کاربر باشد.

تأثیر بر ویتامین‌های اصلی وب

Core Web Vitals تجربه کاربر از یک صفحه وب را در ابعاد مختلف (سرعت بارگذاری، تعامل، پایداری بصری) اندازه‌گیری می‌کند و از آنجایی که کاربران بازیابی bfcache را به عنوان پیمایش سریع‌تر از بارگذاری کامل صفحه تجربه می‌کنند، مهم است که معیارهای Core Web Vitals این را منعکس کنند. از این گذشته، برای کاربر مهم نیست که bfcache فعال بوده یا خیر، او فقط مهم است که پیمایش سریع باشد!

ابزارهایی که معیارهای Core Web Vitals را جمع‌آوری و گزارش می‌دهند، مانند گزارش تجربه کاربری کروم ، بازیابی‌های bfcache را به عنوان بازدیدهای جداگانه از صفحه در مجموعه داده‌های خود در نظر می‌گیرند. و در حالی که APIهای عملکرد وب اختصاصی برای اندازه‌گیری این معیارها پس از بازیابی‌های bfcache وجود ندارد، می‌توانید مقادیر آنها را با استفاده از APIهای وب موجود تخمین بزنید:

  • برای Largest Contentful Paint (LCP) ، از دلتای بین برچسب زمانی رویداد pageshow و برچسب زمانی فریم نقاشی شده بعدی استفاده کنید، زیرا تمام عناصر موجود در فریم به طور همزمان نقاشی می‌شوند. در مورد بازیابی bfcache، LCP و FCP یکسان هستند.
  • برای تعامل با رنگ بعدی (INP) ، به استفاده از Performance Observer فعلی خود ادامه دهید، اما مقدار INP فعلی را به ۰ تنظیم مجدد کنید.
  • برای تغییر چیدمان تجمعی (CLS) ، به استفاده از Performance Observer فعلی خود ادامه دهید، اما مقدار CLS فعلی را به ۰ تنظیم مجدد کنید.

برای جزئیات بیشتر در مورد چگونگی تأثیر bfcache بر هر معیار، به صفحات راهنمای معیارهای Core Web Vitals مراجعه کنید. برای مثال خاصی از نحوه پیاده‌سازی نسخه‌های bfcache از این معیارها، به PR اضافه کردن آنها به کتابخانه JS web-vitals مراجعه کنید.

کتابخانه جاوا اسکریپت web-vitals در معیارهایی که گزارش می‌دهد، از بازیابی‌های bfcache پشتیبانی می‌کند .

منابع اضافی