תמיכה בדפדפנים
דפדפנים מודרניים משהים לפעמים דפים או משמיטים אותם לגמרי כשמשאבים המערכת מוגבלים. בעתיד, הדפדפנים ירצו לעשות זאת באופן יזום כדי לצמצם את צריכת החשמל והזיכרון. Page Lifecycle API מספק וו hooks של מחזור חיים כדי שהדפים יוכלו לטפל בבטחה בהתערבויות האלה בדפדפן בלי להשפיע על חוויית המשתמש. כדאי לעיין ב-API כדי לבדוק אם כדאי להטמיע את התכונות האלה באפליקציה.
רקע
מחזור החיים של אפליקציות הוא דרך מרכזית שבה מערכות הפעלה מודרניות מנהלות משאבים. ב-Android, ב-iOS ובגרסאות האחרונות של Windows, מערכת ההפעלה יכולה להפעיל ולהפסיק אפליקציות בכל שלב. כך הפלטפורמות האלה יכולות לייעל את המשאבים ולחלק אותם מחדש באופן שמניב את התועלת הרבה ביותר למשתמש.
באינטרנט, לא היה בעבר מחזור חיים כזה, והאפליקציות יכולות להמשיך לפעול ללא הגבלת זמן. כשמפעילים מספר גדול של דפי אינטרנט, יכול להיות שיהיה ביקוש יתר על משאבי מערכת קריטיים כמו זיכרון, מעבד (CPU), סוללה ורשת, וכתוצאה מכך חוויית המשתמש תהיה גרועה.
כבר זמן רב יש בפלטפורמת האינטרנט אירועים שקשורים למצבים במחזור החיים, כמו load
, unload
ו-visibilitychange
. עם זאת, האירועים האלה מאפשרים למפתחים להגיב רק לשינויים במצבים במחזור החיים שהמשתמשים יזמו. כדי שהאינטרנט יפעל בצורה מהימנה במכשירים עם הספק נמוך (וגם כדי לנצל את המשאבים בצורה יעילה יותר באופן כללי בכל הפלטפורמות), הדפדפנים צריכים דרך לממש מראש ולתת הקצאה מחדש למשאבי המערכת.
למעשה, דפדפנים כבר נוקטים פעולות פעילות לחיסכון במשאבים בדפים בכרטיסיות ברקע, ודפדפנים רבים (במיוחד Chrome) רוצים להרחיב את הפעולות האלה כדי לצמצם את טביעת הרגל הכוללת שלהם במשאבים.
הבעיה היא שלמפתחים אין דרך להתכונן להתערבויות מהסוג הזה שמבוצעות על ידי המערכת, או אפילו לדעת שהן מתרחשות. כלומר, הדפדפנים צריכים לפעול באופן שמרני, אחרת הם עלולים לגרום לשיבושים בדפי אינטרנט.
Page Lifecycle API מנסה לפתור את הבעיה הזו באמצעות:
- הצגת הקונספט של מצבי מחזור חיים באינטרנט וקביעת סטנדרטים לגביו.
- הגדרת מצבים חדשים שמנוהלים על ידי המערכת ומאפשרים לדפדפנים להגביל את המשאבים שיכולים להיות נצרכים על ידי כרטיסיות מוסתרות או לא פעילות.
- יצירת ממשקי API ואירועים חדשים שמאפשרים למפתחי אתרים להגיב למעברים למצבים החדשים האלה שמנוהלים על ידי המערכת, ומהם.
הפתרון הזה מספק את היכולת לחזות את העתיד, שדרושה למפתחי אינטרנט כדי ליצור אפליקציות עמידות בפני התערבויות מערכתיות, ומאפשר לדפדפנים לבצע אופטימיזציה של משאבי המערכת באופן אגרסיבי יותר, כך שכל משתמשי האינטרנט נהנים ממנו בסופו של דבר.
בהמשך הפוסט נסביר על התכונות החדשות של מחזור החיים של הדף ונראה איך הן קשורות לכל המצבים והאירועים הקיימים בפלטפורמת האינטרנט. בנוסף, נספק המלצות ושיטות מומלצות לגבי סוגי העבודה שמפתחים צריכים לבצע (ולא לבצע) בכל מצב.
סקירה כללית על המצבים והאירועים במחזור החיים של דפים
כל המצבים של מחזור החיים של הדף הם נפרדים ומנוגדים זה לזה, כלומר דף יכול להיות רק במצב אחד בכל פעם. בנוסף, בדרך כלל אפשר לראות את רוב השינויים במצב של מחזור החיים של הדף באמצעות אירועי DOM (החרגות מפורטות בהמלצות למפתחים לכל מצב).
הדרך הקלה ביותר להסביר את המצבים במחזור החיים של הדף, וגם את האירועים שמסמנים את המעברים ביניהם, היא באמצעות תרשים:
מדינות
בטבלה הבאה מוסבר כל מצב בפירוט. בנוסף, מפורטים המצבים האפשריים שיכולים להתרחש לפני ואחרי, וגם האירועים שמפתחים יכולים להשתמש בהם כדי לעקוב אחרי השינויים.
מדינה | תיאור |
---|---|
פעיל |
דף נמצא במצב פעיל אם הוא גלוי ויש לו מיקוד קלט.
מצבים קודמים אפשריים: |
פסיבי |
דף נמצא בסטטוס פסיבי אם הוא גלוי ואין בו מוקד קלט.
מצבים קודמים אפשריים:
המצבים הבאים אפשריים: |
מוסתרת |
דף נמצא בסטטוס מוסתר אם הוא לא גלוי (ולא הוקפא, הושלך או הופסק).
מצבים קודמים אפשריים:
המצבים הבאים אפשריים: |
קפוא |
במצב קפוא, הדפדפן משהה את הביצוע של
משימות
שניתנות להקפאה ב
תורנויות המשימות של הדף, עד שהדף ינוקה מהקיפאון. המשמעות היא שדברים כמו שעונים של JavaScript וקריאות חוזרות (callbacks) של אחזור לא פועלים. משימות שכבר פועלות יכולות להסתיים (החשובה שבהן היא קריאת החזרה (callback) של
דפדפנים מקפיאים דפים כדי לחסוך בשימוש במעבד, בסוללה ובנתונים. הם עושים זאת גם כדי לאפשר ניווט מהיר יותר לאחור או קדימה, וכך למנוע צורך בטעינה מחדש מלאה של הדף.
מצבים קודמים אפשריים:
המצבים הבאים אפשריים: |
Terminated |
דף נמצא במצב הסרה אחרי שהדפדפן התחיל להסיר את הנתונים שנטענו ולנקות את הזיכרון. אי אפשר להתחיל משימות חדשות במצב הזה, וייתכן שמשימות שנמצאות בתהליך יסתיימו אם הן יפעלו יותר מדי זמן.
מצבים קודמים אפשריים:
המצבים הבאים האפשריים: |
Discarded |
דף נמצא במצב discarded כשהדפדפן מסיר ממנו את הנתונים שנטענו כדי לחסוך במשאבים. אי אפשר להריץ משימות, אירועים חוזרים או JavaScript מכל סוג שהוא במצב הזה, כי בדרך כלל השלכת אירועים מתרחשת במצב של אילוצי משאבים, שבהם אי אפשר להפעיל תהליכים חדשים. בסטטוס discarded, הכרטיסייה עצמה (כולל כותרת הכרטיסייה וסמל הדף) בדרך כלל גלויה למשתמש, גם אם הדף לא מופיע.
מצבים קודמים אפשריים:
המצבים הבאים האפשריים: |
אירועים
הדפדפנים שולחים הרבה אירועים, אבל רק חלק קטן מהם מאותת על שינוי אפשרי במצב של מחזור החיים של הדף. בטבלה הבאה מפורטים כל האירועים שקשורים למחזור החיים, ומפורטות המצבים שהם יכולים לעבור אליהם ומהם.
שם | פרטים |
---|---|
focus
|
רכיב DOM קיבל את המיקוד.
הערה: אירוע
מצבים קודמים אפשריים:
המצבים הנוכחיים האפשריים: |
blur
|
רכיב DOM איבד את המיקוד.
הערה: אירוע
הסטטוסים הקודמים האפשריים:
המצבים הנוכחיים האפשריים: |
visibilitychange
|
הערך של השדה
|
freeze
*
|
הדף הוקפא עכשיו. כל משימה ש ניתנת להקפאה בדופים של תור המשימות לא תתחיל.
הסטטוסים הקודמים האפשריים:
המצבים הנוכחיים האפשריים: |
resume
*
|
הדפדפן המשיך בדף מקפיא.
מצבים קודמים אפשריים:
מצבים אפשריים נוכחיים: |
pageshow
|
מתבצע מעבר לרשומה בהיסטוריית הסשנים. יכול להיות שמדובר בטעינה של דף חדש לגמרי או בדף שנלקח ממטמון לדף הקודם/הבא. אם הדף נלקח מהמטמון לדף הקודם/הבא, הערך של המאפיין |
pagehide
|
הנתונים מגיעים מהרשומה בהיסטוריית הסשנים. אם המשתמש מנווט לדף אחר והדפדפן יכול להוסיף את הדף הנוכחי למטמון של הדף הקודם/הבא לשימוש חוזר מאוחר יותר, הערך של המאפיין
הסטטוסים הקודמים האפשריים:
מצבים אפשריים נוכחיים: |
beforeunload
|
החלון, המסמך והמשאבים שלו עומדים לרדת משימוש. המסמך עדיין גלוי והאירוע עדיין ניתן לביטול בשלב הזה.
חשוב: צריך להשתמש באירוע
הסטטוסים הקודמים האפשריים:
הסטטוסים האפשריים הנוכחיים: |
unload
|
הדף עובר פריקת נתונים.
אזהרה: אף פעם לא מומלץ להשתמש באירוע
הסטטוסים הקודמים האפשריים:
הסטטוסים האפשריים הנוכחיים: |
* סימן שמציין אירוע חדש שהוגדר על ידי Page Lifecycle API
תכונות חדשות שנוספו ב-Chrome 68
בתרשים הקודם מוצגות שתי סטטוסים שהמערכת מפעילה ולא המשתמש: קפוא ומושלך. כפי שצוין קודם, דפדפנים כבר מקפיאים מדי פעם כרטיסיות מוסתרות ומבטלים אותן (לפי שיקול דעתם), אבל למפתחים אין דרך לדעת מתי זה קורה.
ב-Chrome 68, מפתחים יכולים עכשיו לזהות מתי כרטיסייה מוסתרת קופאת ומתי היא מפשירה, על ידי האזנה לאירועים freeze
ו-resume
ב-document
.
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
החל מגרסה 68 של Chrome, האובייקט document
כולל עכשיו את הנכס wasDiscarded
ב-Chrome למחשב (אנחנו עוקבים אחרי התמיכה ב-Android בבעיה הזו). כדי לקבוע אם דף הושלך בזמן שהיה בכרטיסייה מוסתרת, אפשר לבדוק את הערך של המאפיין הזה בזמן טעינת הדף (הערה: צריך לטעון מחדש דפים שהושמדו כדי להשתמש בהם שוב).
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
במאמר המלצות למפתחים לכל מצב מוסבר מה חשוב לעשות באירועים freeze
ו-resume
, ואיך לטפל בדפים שנדחים ולהתכונן לכך.
בחלקים הבאים מופיעה סקירה כללית של האופן שבו התכונות החדשות משתלבות באירועים ובמצבים הקיימים בפלטפורמת האינטרנט.
איך לבדוק את המצבים של מחזור החיים של דף בקוד
במצבים פעיל, פסיבי ומוסתר, אפשר להריץ קוד JavaScript שקובע את המצב הנוכחי של מחזור החיים של הדף באמצעות ממשקי API קיימים של פלטפורמות אינטרנט.
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
לעומת זאת, אפשר לזהות את המצבים frozen ו-terminated רק ב-event listener המתאים (freeze
ו-pagehide
) בזמן שהמצב משתנה.
איך עוקבים אחרי שינויים במצב
על סמך הפונקציה getState()
שהוגדרה קודם, אפשר לצפות בכל השינויים במצב של מחזור החיים של הדף באמצעות הקוד הבא.
// Stores the initial state using the `getState()` function (defined above).
let state = getState();
// Accepts a next state and, if there's been a state change, logs the
// change to the console. It also updates the `state` value defined above.
const logStateChange = (nextState) => {
const prevState = state;
if (nextState !== prevState) {
console.log(`State change: ${prevState} >>> ${nextState}`);
state = nextState;
}
};
// Options used for all event listeners.
const opts = {capture: true};
// These lifecycle events can all use the same listener to observe state
// changes (they call the `getState()` function to determine the next state).
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, () => logStateChange(getState()), opts);
});
// The next two listeners, on the other hand, can determine the next
// state from the event itself.
window.addEventListener('freeze', () => {
// In the freeze event, the next state is always frozen.
logStateChange('frozen');
}, opts);
window.addEventListener('pagehide', (event) => {
// If the event's persisted property is `true` the page is about
// to enter the back/forward cache, which is also in the frozen state.
// If the event's persisted property is not `true` the page is
// about to be unloaded.
logStateChange(event.persisted ? 'frozen' : 'terminated');
}, opts);
הקוד הזה מבצע שלוש פעולות:
- הגדרת המצב הראשוני באמצעות הפונקציה
getState()
. - הגדרת פונקציה שמקבלת מצב נוסף, ואם יש שינוי, מתעדת את שינויי המצב במסוף.
- הוספת פונקציות תיעוד של אירועי מעקב לכל אירועי מחזור החיים הנדרשים, שמפעילים בתורו את
logStateChange()
ומעבירים את המצב הבא.
חשוב לזכור לגבי הקוד שכל פונקציות ה-event listener מתווספות ל-window
, וכולן מעבירות את הערך {capture: true}
.
יכולות להיות לכך כמה סיבות:
- לא לכל האירועים במחזור החיים של הדף יש את אותו יעד. האירועים
pagehide
ו-pageshow
מופעלים ב-window
, האירועיםvisibilitychange
,freeze
ו-resume
מופעלים ב-document
, והאירועיםfocus
ו-blur
מופעלים ברכיבי ה-DOM המתאימים. - רוב האירועים האלה לא עוברים דרך כל ההורים, כלומר אי אפשר להוסיף רכיבי מעקב אחרי אירועים שלא מתעדים אותם לאובייקט אב משותף ולעקוב אחרי כולם.
- שלב הצילום מתבצע לפני שלבי היעד או הבועה, כך שהוספת מאזינים שם עוזרת לוודא שהם יפעלו לפני שקוד אחר יוכל לבטל אותם.
המלצות למפתחים לכל מצב
כמפתחים, חשוב להבין את המצבים של מחזור החיים של הדף וגם לדעת איך לזהות אותם בקוד, כי סוג העבודה שצריך לבצע (וגם לא לבצע) תלוי במידה רבה במצב שבו הדף נמצא.
לדוגמה, ברור שאין טעם להציג למשתמש התראה זמנית אם הדף נמצא במצב מוסתר. הדוגמה הזו די ברורה, אבל יש המלצות אחרות שלא כל כך ברורות ששווה לציין.
מדינה | המלצות למפתחים |
---|---|
Active |
המצב פעיל הוא הזמן הקריטי ביותר עבור המשתמש, ולכן זהו הזמן החשוב ביותר שהדף שלכם יגיב לקלט של המשתמש. צריך להפחית את רמת העדיפות של כל עבודה שאינה קשורה לממשק המשתמש שעלולה לחסום את השרשור הראשי, ולהעביר אותה ל תקופות חוסר פעילות או להעביר אותה ל-web worker. |
Passive |
במצב פסיבי, המשתמש לא מקיים אינטראקציה עם הדף, אבל הוא עדיין יכול לראות אותו. כלומר, עדכוני ממשק המשתמש והאנימציות עדיין אמורים להיות חלקים, אבל התזמון שבו העדכונים האלה מתרחשים פחות קריטי. כשהסטטוס של הדף משתנה מפעיל לפסיבי, זה הזמן המתאים לשמור את המצב של האפליקציה שלא נשמר. |
כשהדף עובר מסטטוס פסיבי לסטטוס מוסתר, יכול להיות שהמשתמש לא יתקשר איתו שוב עד שהוא ייטען מחדש. בנוסף, המעבר לסטטוס hidden הוא לרוב שינוי המצב האחרון שיזוהה באופן מהימן על ידי המפתחים (הדבר נכון במיוחד בניידים, כי משתמשים יכולים לסגור כרטיסיות או את אפליקציית הדפדפן עצמה, ובמקרים כאלה האירועים המשמעות היא שצריך להתייחס למצב hidden כסיום הסביר של הסשן של המשתמש. במילים אחרות, לשמור את מצב האפליקציה שלא נשמר ולשלוח את נתוני הניתוח שלא נשלחו. כמו כן, כדאי להפסיק לבצע עדכונים של ממשק המשתמש (כי המשתמש לא יראה אותם), ולהפסיק משימות שהמשתמש לא רוצה שיפעלו ברקע. |
|
Frozen |
במצב frozen, משימות שאפשר להקפיא ב תורנויות המשימות מושהות עד שההקפאה של הדף תוסר – ויכול להיות שההקפאה לא תוסר אף פעם (למשל, אם הדף נמחק). כלומר, כשהדף עובר מסטטוס מוסתר לסטטוס קפוא, חובה לעצור את כל הטיימרים או לבטל את כל החיבורים שעשויים להשפיע על כרטיסיות פתוחות אחרות באותו מקור, או על היכולת של הדפדפן להעביר את הדף ל מטמון של דפים קודמים/לאחור. במיוחד חשוב:
כדאי גם לשמור את כל מצב התצוגה הדינמי (למשל, מיקום הגלילה בתצוגת רשימה אינסופית) ב-
אם הדף עובר מקפוא למוסתר, תוכלו לפתוח מחדש את כל החיבורים הסגורים או להפעיל מחדש את כל הסקרים שעצרתם כשהדף קפא בהתחלה. |
Terminated |
בדרך כלל אין צורך לבצע פעולה כלשהי כשדף עובר למצב הושעה. מאחר שדפים שמנוהלים לטעינה מחדש כתוצאה מפעולה של משתמש תמיד עוברים דרך המצב hidden לפני שהם נכנסים למצב terminated, המצב hidden הוא המקום שבו צריך לבצע את הלוגיקה של סיום הסשן (למשל, שמירת מצב האפליקציה ודוחות ל-Analytics). בנוסף (כפי שצוין בהמלצות למצב מוסתר), חשוב מאוד שהמפתחים יבינו שלא ניתן לזהות באופן מהימן את המעבר למצב הסתיים במקרים רבים (במיוחד בנייד), ולכן מפתחים שתלויים באירועי סיום (למשל |
Discarded |
המפתחים לא יכולים לראות את המצב discarded בזמן שהדף מבוטל. הסיבה לכך היא שבדרך כלל דפים נמחקים עקב אילוצים של משאבים, וברוב המקרים פשוט לא ניתן לבטל את ההקפאה של דף רק כדי לאפשר להפעלת סקריפט להגיב לאירוע מחיקה. לכן, כדאי להתכונן לאפשרות של ביטול השינוי ממוסתר לקפוא, ואז תוכלו להגיב לשחזור של דף שבוטל בזמן הטעינה של הדף על ידי בדיקת |
שוב, מכיוון שההטמעה של מהימנות וסדר האירועים במחזור החיים לא עקבית בכל הדפדפנים, הדרך הקלה ביותר לפעול בהתאם להמלצות בטבלה היא להשתמש ב-PageLifecycle.js.
ממשקי API מדור קודם של מחזור חיים שצריך להימנע מהם
במידת האפשר, כדאי להימנע מהאירועים הבאים.
אירוע ההסרה
מפתחים רבים מתייחסים לאירוע unload
כקריאה חוזרת מובטחת ומשתמשים בו כאות לסיום הסשן כדי לשמור את המצב ולשלוח נתוני ניתוח, אבל הדבר לא מהימן במיוחד, במיוחד בניידים. האירוע unload
לא מופעל בהרבה מצבים אופייניים של ביטול טעינה, כולל סגירת כרטיסייה ממעבר הכרטיסיות בנייד או סגירת אפליקציית הדפדפן ממעבר האפליקציות.
לכן, תמיד עדיף להסתמך על האירוע visibilitychange
כדי לקבוע מתי הסשן מסתיים, ולחשוב על המצב המוסתר בתור הזמן האחרון המהימן לשמירת נתוני האפליקציה והמשתמשים.
בנוסף, עצם הנוכחות של טיפול באירוע unload
רשום (דרך onunload
או addEventListener()
) יכולה למנוע מדפדפנים לשמור דפים במטמון לדף הקודם/הבא כדי לאפשר טעינה מהירה יותר של דפים קודמים ודפים הבאים.
בכל הדפדפנים המודרניים, מומלץ תמיד להשתמש באירוע pagehide
כדי לזהות אפשרויות של ביטול טעינת דפים (כלומר המצב terminated) במקום באירוע unload
. אם אתם צריכים לתמוך ב-Internet Explorer בגרסאות 10 ומטה, כדאי לזהות את האירוע pagehide
ולהשתמש ב-unload
רק אם הדפדפן לא תומך ב-pagehide
:
const terminationEvent = 'onpagehide' in self ? 'pagehide' : 'unload';
window.addEventListener(terminationEvent, (event) => {
// Note: if the browser is able to cache the page, `event.persisted`
// is `true`, and the state is frozen rather than terminated.
});
האירוע beforeunload
לאירוע beforeunload
יש בעיה דומה לבעיה של האירוע unload
, מאחר שבעבר נוכחות של אירוע beforeunload
יכלה למנוע מדפים לעמוד בדרישות לשמירה במטמון לדף הקודם/הבא. בדפדפנים מודרניים אין הגבלה כזו. עם זאת, דפדפנים מסוימים לא יפעילו את האירוע beforeunload
כצעד זהירות כשמנסים להעביר דף למטמון של 'הקודם'/'הבא', כלומר האירוע לא מהימן כאות לסיום הסשן.
בנוסף, בדפדפנים מסוימים (כולל Chrome) נדרשת אינטראקציה של משתמש בדף לפני שהאירוע beforeunload
יופעל, מה שמשפיע עוד יותר על האמינות שלו.
אחד ההבדלים בין beforeunload
לבין unload
הוא שיש שימושים לגיטימיים ב-beforeunload
. לדוגמה, כשרוצים להזהיר את המשתמש שיש לו שינויים שלא נשמרו, והם יאבדו אם הוא ימשיך לפרוק את הדף.
יש סיבות לגיטימיות לשימוש ב-beforeunload
, לכן מומלץ להוסיף רק מאזינים ל-beforeunload
כשלמשתמש יש שינויים שלא נשמרו, ואז להסיר אותם מיד אחרי שהם נשמרים.
במילים אחרות, אל תעשו את זה (כי הפעולה הזו מוסיפה מאזין beforeunload
ללא תנאי):
addEventListener('beforeunload', (event) => {
// A function that returns `true` if the page has unsaved changes.
if (pageHasUnsavedChanges()) {
event.preventDefault();
// Legacy support for older browsers.
return (event.returnValue = true);
}
});
במקום זאת, צריך לעשות את הפעולות הבאות (כי הקוד הזה מוסיף את המאזין beforeunload
רק כשצריך, ומסיר אותו כשלא צריך):
const beforeUnloadListener = (event) => {
event.preventDefault();
// Legacy support for older browsers.
return (event.returnValue = true);
};
// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
addEventListener('beforeunload', beforeUnloadListener);
});
// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
removeEventListener('beforeunload', beforeUnloadListener);
});
שאלות נפוצות
למה אין מצב 'טעינה'?
ב-Page Lifecycle API, המצבים מוגדרים כמצבים נפרדים ולא חופפים. מכיוון שדף יכול להיטען במצב פעיל, פסיבי או מוסתר, ויכול לשנות מצבים – או אפילו להסתיים – לפני שהוא מסיים את הטעינה, אין טעם להגדיר מצב טעינה נפרד בתוך הפרדיגמה הזו.
הדף שלי מבצע משימות חשובות גם כשהוא מוסתר. איך אפשר למנוע את הקפאתו או השמדתו?
יש הרבה סיבות לגיטימיות לכך שדפי אינטרנט לא צריכים להקפיא בזמן שהם פועלים במצב מוסתר. הדוגמה הברורה ביותר היא אפליקציה שמשמיעה מוזיקה.
יש גם מצבים שבהם יכול להיות מסוכן ל-Chrome לבטל דף, למשל אם הוא מכיל טופס עם קלט של משתמש שלא נשלח, או אם יש לו טיפול beforeunload
שמציג אזהרה כשהדף פורק.
בשלב הזה, Chrome יהיה שמרני בביטול דפים, ויבטל אותם רק אם הוא בטוח שהפעולה הזו לא תשפיע על המשתמשים. לדוגמה, דפים שנצפו בהם פעולות כלשהן מהפעולות הבאות בזמן שהם במצב מוסתר לא יוסרו, אלא אם יהיו הגבלות קיצוניות על המשאבים:
- הפעלת אודיו
- שימוש ב-WebRTC
- עדכון כותרת הטבלה או הסמל של הטבלה
- הצגת התראות
- שליחת התראות
כדי לקבל מידע על התכונות הנוכחיות של הרשימה שמשמשות לקביעת אם אפשר להקפיא או למחוק כרטיסייה בבטחה, אפשר לעיין במאמר שיטות ניתוח נתונים להקפאה ולמחיקה ב-Chrome.
מטמון לדף הקודם/הבא הוא מונח שמתאר אופטימיזציה של הניווט שחלק מהדפדפנים מטמיעים, כדי שהשימוש בלחצני 'הקודם' ו'הבא' יהיה מהיר יותר.
כשמשתמש עוזב דף, הדפדפנים האלה מקפיאים גרסה של הדף הזה כדי שניתן יהיה להמשיך אותו במהירות במקרה שהמשתמש יחזור לדף באמצעות הלחצנים 'הקודם' או 'הבא'. חשוב לזכור שהוספת גורם מטפל באירועים מסוג unload
מונעת את האפשרות לבצע את האופטימיזציה הזו.
מבחינה פונקציונלית, ההקפאה הזו זהה להקפאה שמתבצעת בדפדפנים כדי לחסוך בצריכת המעבד או הסוללה. לכן, היא נחשבת לחלק מסטטוס מחזור החיים frozen.
איך אפשר לשמור נתונים ב-IndexedDB אם אי אפשר להריץ ממשקי API אסינכררוניים במצבים 'קפוא' או 'הושעה'?
בסטטוסים 'מושהה' ו'הושעה', משימות שאפשר להשהות בתורנויות המשימות של הדף מושהות. המשמעות היא שלא ניתן להשתמש באופן מהימן בממשקי API אסינכררוניים ומבוססי קריאה חוזרת (callback), כמו IndexedDB.
בעתיד נוסיף שיטה commit()
לאובייקטים מסוג IDBTransaction
, שתאפשר למפתחים לבצע טרנזקציות לכתיבה בלבד, ללא צורך בקריאות חזרה (callbacks). במילים אחרות, אם המפתח רק כותב נתונים ל-IndexedDB ולא מבצע עסקה מורכבת שמכילה קריאות וכתיבה, השיטה commit()
תוכל להסתיים לפני שתורנויות המשימות יושהו (בהנחה שמסד הנתונים של IndexedDB כבר פתוח).
עם זאת, למפתחים שיש להם קוד שצריך לפעול כבר עכשיו יש שתי אפשרויות:
- שימוש ב-Session Storage: Session Storage הוא סנכרוני ונשמר גם אחרי שמחרימים דפים.
- שימוש ב-IndexedDB מה-service worker: ה-service worker יכול לאחסן נתונים ב-IndexedDB אחרי שהדף הופסק או הושלך. אפשר לשלוח נתונים ל-service worker דרך
postMessage()
ב-event listener שלfreeze
אוpagehide
, וה-service worker יכול לטפל בשמירת הנתונים.
בדיקת האפליקציה בסטטוסים 'מושהה' ו'נדחה'
כדי לבדוק איך האפליקציה מתנהגת במצבים 'הקפאה' ו'מחיקה', אפשר להיכנס לדף chrome://discards
כדי להקפיא או למחוק כרטיסיות פתוחות.
כך תוכלו לוודא שהדף מטפל בצורה נכונה באירועים freeze
ו-resume
, וגם בדגל document.wasDiscarded
כשדפים נטענים מחדש אחרי שהם נמחקים.
סיכום
מפתחים שרוצים לכבד את משאבי המערכת של המכשירים של המשתמשים שלהם צריכים לפתח את האפליקציות שלהם תוך התחשבות במצבים של מחזור החיים של הדף. חשוב מאוד שדפי אינטרנט לא צורכים משאבי מערכת מוגזמים במצבים שהמשתמש לא מצפה להם.
ככל שיותר מפתחים יתחילו להטמיע את ממשקי ה-API החדשים של מחזור החיים של דפים, כך יהיה בטוח יותר לדפדפנים להקפיא ולמחוק דפים שלא בשימוש. המשמעות היא שהדפדפנים צורכים פחות זיכרון, מעבד (CPU), סוללה ומשאבי רשת, וזה יתרון למשתמשים.