کاربران دکمه بازگشت را بیش از هر کنترل دیگری در مرورگر فشار می‌دهند. آن‌ها انتظار دارند صفحه قبلی بلافاصله و دقیقاً همان‌جایی که رها کرده بودند، ظاهر شود. مرورگرهای مدرن با استفاده از حافظه موقت بازگشت/جلو (back/forward cache) یا همان bfcache، این انتظار را برآورده می‌کنند. مرورگر به‌جای نابود کردن صفحه هنگام خروج از آن، آن را در حافظه منجمد (freeze) می‌کند. وقتی باز می‌گردید، مرورگر یک اسنپ‌شات (snapshot) از آن را بازیابی می‌کند. در این حالت، مرورگر از تجزیه HTML، اجرای مجدد جاوااسکریپت و محاسبه مجدد چیدمان (layout) صرف‌نظر می‌کند. نتیجه کار آنی به نظر می‌رسد، زیرا صفحه هرگز کاملاً از بین نرفته است.

عملکرد واقعی bfcache چیست

بارگذاری معمولی یک صفحه، فرآیند سنگینی است. مرورگر باید منابع را فراخوانی کند، HTML را توکنایز کند، DOM را بسازد، اسکریپت‌ها را اجرا کند، استایل‌ها را حل کند، چیدمان را انجام دهد، پیکسل‌ها را رنگ‌آمیزی کند و لایه‌ها را با هم ترکیب کند. bfcache تقریباً با نگه داشتن صفحه در یک حالت منجمد در RAM، از تمام این مراحل عبور می‌کند. این یک کش دیسکی نیست. صفحه رندر شده، شامل هپ (heap) جاوااسکریپت، موقعیت اسکرول و وضعیت فرم، در حالی که کاربر صفحه بعدی را می‌خواند، در حافظه باقی می‌ماند. وقتی کاربر روی دکمه بازگشت کلیک می‌کند، مرورگر اسنپ‌شات را از حالت انجماد خارج کرده و رویداد pageshow را اجرا می‌کند. صفحه بدون تماس با شبکه یا انجام مجدد چیدمان از صفر، از سر گرفته می‌شود. برای کاربرانی که از دستگاه‌های کند یا اتصالات ناپایدار استفاده می‌کنند، تفاوت بین بازیابی از طریق bfcache و بارگذاری مجدد می‌تواند صدها میلی‌ثانیه یا بیشتر باشد.

چه چیزهایی باعث از کار افتادن آن می‌شود

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

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

دو صفحه با شکست مواجه شدند. صفحه‌ای که دارای شنونده رویداد unload بود، نتوانست بازیابی شود. صفحه‌ای که یک اتصال WebSocket باز داشت نیز مسدود شد. این دو شکست، به تله‌هایی اشاره دارند که هر روز سایت‌های واقعی در محیط عملیاتی با آن‌ها روبرو می‌شوند.

تله‌ی رویداد unload

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

جایگزین آن pagehide است. این رویداد هم زمانی که صفحه برای bfcache منجمد می‌شود و هم زمانی که واقعاً در حال حذف شدن است، اجرا می‌شود. اگر نیاز دارید بین این دو تمایز قائل شوید، ویژگی event.persisted زمانی که صفحه در حال رفتن به سمت bfcache است، مقدار true دارد. با این حال، برای اکثر وظایف پاکسازی، pagehide هر دو مسیر را پوشش می‌دهد. تمام منطق پاکسازی را از unload خارج کرده و به pagehide منتقل کنید. سپس تمام شنونده‌های unload را به‌طور کامل حذف کنید، از جمله آن‌هایی که در قطعه‌کدهای آنالیتیکس شخص ثالث یا پلاگین‌های قدیمی پنهان شده‌اند.

تله‌های اتصال فعال

یک اتصال باز شبکه یا ذخیره‌سازی، نشان‌دهنده این است که صفحه شما هنوز در حال انجام کار واقعی است. مرورگر در لحظه ناوبری (navigation)، منابع فعال را فهرست می‌کند. اگر یک WebSocket باز، یک اتصال همتا-به-همتا (peer connection) فعال در WebRTC یا یک اتصال IndexedDB باقی‌مانده پیدا کند، فرآیند انجماد را لغو کرده و صفحه را به‌طور معمول از بین می‌برد. تا زمانی که ممکن است بایت‌ها همچنان در حال جریان باشند، نمی‌توان به اسنپ‌شات اعتماد کرد.

شما باید این منابع را داخل یک شنونده pagehide ببندید. متد close مربوط به WebSocket خود را فراخوانی کنید. اتصالات WebRTC peer را قطع کنید. هرگونه تراکنش معلق در IndexedDB را لغو یا نهایی (commit) کنید. اگر اپلیکیشن شما هنگام بازگشت کاربر به این کانال‌ها نیاز دارد، آن‌ها را داخل pageshow دوباره باز کنید. این الگوی «بستن در pagehide و بازیابی در pageshow»، صفحه را برای ناوبری آنیِ بازگشت، بدون از دست دادن قابلیت‌ها، واجد شرایط نگه می‌دارد.

غافلگیری no-store

سال‌ها، باور عمومی بر این بود که Cache-Control: no-store مانع از bfcache می‌شود. کروم این رفتار را در سال ۲۰۲۵ تغییر داد. اکنون صفحه‌ای که با no-store سرو می‌شود می‌تواند وارد bfcache شود. مرورگر تنها در صورتی اسنپ‌شات منجمد شده را حذف می‌کند که وضعیت‌های احراز هویت یا کوکی‌ها به گونه‌ای تغییر کنند که وضعیت ذخیره‌شده را باطل کنند. اگر از no-store به عنوان...