שיטות מומלצות לטעינה מדורגת

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

זה הזמן לקפל

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

טעינה מושהית מעכבת את טעינת המשאבים עד שה-DOM יהיה אינטראקטיבי, כשהסקריפטים מסיימים לטעון ולהתחיל בהפעלה. זה בסדר כשמדובר בתמונות בחלק הנגלל, אבל צריך לטעון משאבים קריטיים שנמצאים בחלק העליון והקבוע באמצעות הרכיב <img> הסטנדרטי כדי שהם יוצגו בהקדם האפשרי.

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

בנוסף, לא כדאי להיות קפדניים כל כך בנוגע לקו המתקפל בתור הסף להפעלת טעינה מדורגת. יכול להיות שהיא אידיאלית למטרות יצירת אזור חיץ-מרחק מתחת לקיפול, כדי שהתמונות יתחילו להיטען הרבה לפני שהמשתמש יגלול אותן לאזור התצוגה. לדוגמה, ממשק ה-API של Intersection Observer מאפשר לציין מאפיין rootMargin באובייקט אפשרויות כשיוצרים מכונת IntersectionObserver חדשה. למעשה, רכיבים חוצים את שטח האחסון הזמני, מה שמפעיל התנהגות של טעינה מדורגת לפני שהרכיב נמצא באזור התצוגה:

let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
  // lazy-loading image code goes here
}, {
  rootMargin: "0px 0px 256px 0px"
});

אם הערך של rootMargin נראה דומה לערכים שציינתם במאפיין margin של CSS, הסיבה לכך היא! במקרה הזה, השוליים התחתוניים של הרכיב שנמדד (אזור התצוגה של הדפדפן כברירת מחדל, אבל אפשר לשנות אותו לרכיב ספציפי באמצעות המאפיין root) מורחבים ב-256 פיקסלים. כלומר, פונקציית הקריאה החוזרת תפעל כשרכיב התמונה נמצא בטווח של עד 256 פיקסלים מאזור התצוגה, והתמונה תתחיל להיטען לפני שהמשתמש יראה אותה בפועל.

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

שינוי פריסה ו-placeholders

טעינה מדורגת של מדיה עלולה לגרום לתזוזה בפריסה אם לא משתמשים ב-placeholders. השינויים האלה עלולים לבלבל את המשתמשים ולהפעיל פעולות פריסת DOM יקרות שצורכות משאבי מערכת ותורמים ל-jank. לכל הפחות, כדאי להשתמש ב-placeholder בצבע אחיד עם אותם מידות כמו תמונת היעד, או להשתמש בטכניקות כמו LQIP או SQIP שמרמזות על תוכן פריט מדיה לפני שהוא נטען.

בתגים <img>, src צריך להפנות ל-placeholder עד שהמאפיין מתעדכן עם כתובת ה-URL הסופית של התמונה. משתמשים במאפיין poster ברכיב <video> כדי להצביע על תמונה מסוג placeholder. בנוסף, צריך להשתמש במאפייני width ו-height גם בתג <img> וגם בתג <video>. כך ניתן להבטיח שהמעבר מ-placeholders לתמונות סופיות לא ישנה את גודל הרכיב שעבר רינדור במהלך טעינת המדיה.

עיכובים בפענוח התמונה

טעינה של תמונות גדולות ב-JavaScript ושחרורן ב-DOM יכולות ליצור קשר ב-thread הראשי, וכתוצאה מכך ממשק המשתמש לא מגיב למשך זמן קצר במהלך הפענוח. פענוח של תמונות באופן אסינכרוני באמצעות שיטת decode לפני ההכנסה שלהן ל-DOM יכול לצמצם את נקודות ה-jank האלה. אבל חשוב להיזהר: האפשרות הזו לא זמינה עדיין בכל מקום, והיא מגבירה את המורכבות ללוגיקת הטעינה הדרגתית. אם ברצונך להשתמש בו, עליך לבדוק זאת. כך אפשר להשתמש ב-Image.decode() כחלופה:

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

if ("decode" in newImage) {
  // Fancy decoding logic
  newImage.decode().then(function() {
    imageContainer.appendChild(newImage);
  });
} else {
  // Regular image load
  imageContainer.appendChild(newImage);
}

היכנסו לקישור הזה ל-CodePen כדי לראות קוד שדומה לדוגמה הזו בפעולה. אם רוב התמונות קטנות יחסית, אולי זה לא יהיה הרבה בשבילך, אבל זה בהחלט יכול לעזור לצמצם את עומסי ה-jank במהלך טעינה מדורגת של תמונות גדולות והכנסתן ל-DOM.

כשדברים לא נטענים

לפעמים משאבי מדיה לא נטענים מסיבה כלשהי או מסיבה אחרת, ומופיעות שגיאות. מתי זה עשוי לקרות? זה תלוי, אבל הנה תרחיש היפותטי אחד: יש לכם מדיניות שמירת HTML במטמון למשך פרק זמן קצר (למשל, חמש דקות), והמשתמש מבקר באתר או משתמש השאיר כרטיסייה לא פעילה פתוחה למשך פרק זמן ארוך (למשל, כמה שעות) והוא חוזר כדי לקרוא את התוכן. בשלב כלשהו בתהליך הזה מתבצעת פריסה מחדש. במהלך הפריסה הזו, השם של משאב תמונה משתנה בגלל ניהול גרסאות שמבוסס על גיבוב, או שהוא מוסר לגמרי. כשהמשתמש טוען באופן מדורג את התמונה, המשאב לא זמין וכתוצאה מכך נכשל.

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

var newImage = new Image();
newImage.src = "my-awesome-image.jpg";

newImage.onerror = function(){
  // Decide what to do on error
};
newImage.onload = function(){
  // Load the image
};

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

יכולים להיות גם תרחישים אחרים. בכל מקרה, אף פעם לא כדאי לאותת למשתמש כשמתרחשת שגיאה, ואולי לנקוט פעולה כלשהי אם משהו משתבש.

זמינות של JavaScript

אין להניח ש-JavaScript תמיד זמין. אם אתם מתכוונים לטעון תמונות באופן מדורג, כדאי להציע תגי עיצוב של <noscript>, שיציגו תמונות במקרה ש-JavaScript לא זמין. הדוגמה הפשוטה ביותר לחלופה הכי פשוטה כוללת שימוש ברכיבי <noscript> כדי להציג תמונות אם JavaScript מושבת:

אני תמונה!

אם JavaScript מושבת, המשתמשים יראו גם את תמונת ה-placeholder וגם את התמונה שכוללת את רכיבי <noscript>. כדי לעקוף את הבעיה, צריך למקם מחלקה של no-js בתג <html>, כך:

<html class="no-js">

לאחר מכן, צריך להציב שורה אחת של סקריפט מוטבע ב-<head> לפני שמבקשים גיליונות סגנונות באמצעות תגי <link> שמסירים את המחלקה no-js מהרכיב <html> אם JavaScript מופעל:

<script>document.documentElement.classList.remove("no-js");</script>

לסיום, השתמשו ב-CSS מסוים כדי להסתיר רכיבים עם מחלקה של עצלות כאשר JavaScript לא זמין:

.no-js .lazy {
  display: none;
}

הפעולה הזו לא מונעת טעינה של תמונות placeholder, אבל התוצאה רצויה יותר. אנשים שהשביתו את JavaScript מקבלים משהו נוסף מאשר תמונות placeholder, וזה טוב יותר מ-placeholders וללא תוכן תמונות משמעותי.