טיפים לעבודה יעילה עם bfcache

30/08/2023

אחד הטריקים של דפדפן כדי להציג עמודים מהר יותר נקרא bfcache. זה בעצם זיכרון מטמון בו הדפדפן שומר את העמוד ואת כל מצב ה JavaScript של העמוד ביציאה ממנו, כאילו העמוד עדיין פעיל באיזה "טאב" נסתר. בלחיצה על כפתור "אחורה" הדפדפן מושך מהמטמון את העמוד ואת כל זיכרון ה JavaScript כדי שמשתמשים יוכלו להמשיך לעבוד על העמוד במהירות ומאותה נקודה.

בתור מתכנתים אנחנו שמחים על כל התנהגות של הדפדפן שמאיצה את האתרים שלנו, אבל גם צריכים להיות מודעים לכמה אתגרים ספציפיים סביב טעינת עמוד מ bfcache.

1. המחשת השימוש ב bfcache

היכנסו לקישור הבא: https://cdpn.io/pen/debug/JjegWao

תוכלו לראות שם מספרים יורדים מ 9 עד 1, ואז במקום 0 יופיע טקסט ומתחת לטקסט יש לינק. לוחצים על הלינק ואז לוחצים "אחורה" בדפדפן, ותוכלו לראות שהדפדפן לא יריץ את המספרים אחורה שוב וישר יציג את הטקסט, בדיוק מהנקודה בה עצרנו.

זה קוד העמוד אגב מוטמע כאן:

2. איך יודעים אם העמוד נטען מה Cache

דפדפנים מספקים לנו API כדי להבין איך העמוד נטען והאם הוא הגיע מה cache. האירוע pageshow יישלח ל window מיד אחרי load ובכל פעם שהעמוד נטען מה Cache. לאירוע זה יש מאפיין בשם persisted, ואם ערכו true אנחנו יודעים שהעמוד הגיע מה cache. בעזרת אירוע זה אני יכול להציג הודעה ספציפית למשתמש שהגיע אחרי לחיצה על כפתור "אחורה", כמו בדוגמה הבאה:

https://cdpn.io/pen/debug/BaGXWGJ

קוד הדוגמה:

let countdown = 10;

function tick() {
  countdown -= 1;
  if (countdown > 0) {
    document.getElementById("app").innerHTML = `${countdown}`;
    return setTimeout(tick, 1000);
  }

  document.getElementById("app").innerHTML = `
  <h1>Hello Vanilla!</h1>
  <div>
    We use the same configuration as Parcel to bundle this sandbox, you can find more
    info about Parcel 
    <a href="https://parceljs.org" target="_blank" rel="noopener noreferrer">here</a>.
  </div>
  `;
}

window.addEventListener('pageshow', (ev) => {
  console.dir(ev);
  if (ev.persisted) {
    // page loaded from cache after "back"
    document.getElementById("app").innerHTML = `Did you find what you were looking for?`
  }
});

setTimeout(tick, 0);

3. איך לגרום לעמוד לא להיטען מה Cache

יש מספר דברים שאנחנו יכולים לעשות בעמוד שימנעו מדפדפן לשמור את העמוד שלנו ב bfcache, ולכן רוב הזמן אלה דברים שנרצה להימנע מהם:

  1. רוב הדפדפנים לא ישמרו את העמוד ב bfcache אם מוגדר קוד טיפול באירוע unload.

  2. דפדפן פיירפוקס לא ישמור את העמוד ב bfcache אם מוגדר קוד טיפול באירוע beforeunload.

  3. דפדפנים לא ישמרו את העמוד ב bfcache אם השרת שולח את הכותרת Cache-Control: no-store.

  4. חלק מהדפדפנים לא ישמרו עמוד ב bfcache אם יש לו חיבורים פתוחים או בקשות רשת פתוחות (לדוגמה חיבור ל IndexDB, ממתין לתשובה של fetch, ממתין לתשובה של XMLHttpRequest או מחזיק WebSocket פתוח). אירוע בשם pagehide יישלח לעמוד שלכם בדיוק לפני שמשתמשים גולשים החוצה ממנו, ואתם יכולים להשתמש באירוע זה כדי לסגור חיבורים ולבטל בקשות פתוחות.

בדוגמה האחרונה אני משתמש באירוע pagehide כדי להחזיר את שעון ה 10 שניות למצבו המקורי, ומתחיל להריץ את השעון אחורה באירוע pageshow במקום בטעינה של העמוד. בצורה כזאת העמוד עדיין נטען מה bfcache, אבל בניווט אחורה אליו משתמשים יצטרכו לחכות שוב 10 שניות עד שיראו את התוכן:

https://cdpn.io/pen/debug/vYQoxMx

הקוד:

let countdown = 10;

function tick() {
  countdown -= 1;
  if (countdown > 0) {
    document.getElementById("app").innerHTML = `${countdown}`;
    return setTimeout(tick, 1000);
  }

  document.getElementById("app").innerHTML = `
  <h1>Hello Vanilla!</h1>
  <div>
    We use the same configuration as Parcel to bundle this sandbox, you can find more
    info about Parcel 
    <a href="https://parceljs.org" target="_blank" rel="noopener noreferrer">here</a>.
  </div>
  `;  
}

window.addEventListener('pagehide', (ev) => {
  countdown = 10;
  document.getElementById("app").innerHTML = `${countdown}`
});

window.addEventListener('pageshow', (ev) => {
  setTimeout(tick, 0);
});