אופטימיזציה של תמונות באמצעות ההנחיה בנושא תמונות זוויתיות

Kara Erickson
Kara Erickson
Leena Sohoni
Leena Sohoni

במאי 2022, צוותי Aurora ו-Angular הודיעו על שיתוף פעולה בנושא הוראת תמונה ל-Angular. ההנחיה הזו שוחררה לאחרונה בתצוגה מקדימה למפתחים כחלק מ-Angular v14.2. בפוסט הזה נסביר איך ההנחיה החדשה לתמונות, NgOptimizedImage, תומכת באופטימיזציה של תמונות ב-Angular.

רקע

תמונות הן רכיב נפוץ וחיוני בחוויית המשתמש באינטרנט, ו99.9% מדפי האינטרנט יוצרים בקשות לתמונה אחת או יותר. תמונות הן גם הגורם המשמעותי ביותר במשקל הדף, והן מהוות חציון של 982 קילובייט לכל דף.

בגלל המספר והגודל ההולכים וגדלים שלהן, תמונות יכולות לפגוע בביצועים של דפי אינטרנט ולהשפיע על המדדים של דוח המדדים הבסיסיים של חוויית המשתמש (Core Web Vitals). ב-79.4% מהדפים במחשב, תמונה הייתה הרכיב של 'המהירות שבה נטען רכיב התוכן הכי גדול' (LCP) בשנת 2021. לכן, רבים מאיתנו משקיעים מאמצים רבים כדי ליצור תמונות שמותאמות בצורה אופטימלית.

צוות Aurora מאמין בשימוש ביכולות של מסגרות כדי לספק פתרונות מובנים לאתגרים נפוצים בפיתוח. הניסיון הראשון שלהם בתחום אופטימיזציית התמונות היה רכיב התמונה של Next.js. הם ראו ברכיב הזה זירת ניסוי כדי לבדוק אם שיפור חוויית המפתחים (DX) של אופטימיזציית תמונות יוביל לשיפור הביצועים של אפליקציות נוספות שמשתמשות במסגרות.

קבוצת התוצאות הראשונה ממשתמש Next.js‏ Leboncoin הייתה מעודדת. ב-Leboncoin ראו שיפור משמעותי במדד LCP (מ-2.4 שניות ל-1.7 שניות) אחרי שהתחילו להשתמש ב-next/image. ההטמעה של next/image בקהילה לאחר מכן תרמה לעלייה במספר מקורות ה-Next.js שעומדים בערכי הסף של LCP. בקרוב הגיעו בקשות לתכונות דומות במסגרות אחרות, אחת מהן היא Angular.

לכן, ב-Aurora התייעצנו עם Angular ו-Nuxt כדי ליצור אב טיפוס של רכיבי תמונה לפלטפורמות האלה. רכיב התמונה של Nuxt שוחרר בשנה שעברה. עכשיו הוספנו את ההנחיה לתמונות ב-Angular‏ (NgOptimizedImage) כדי להוסיף את הגדרות ברירת המחדל של אופטימיזציית התמונות ל-Angular.

הזדמנות

Angular הוא אחד מסביבות הפיתוח המובילות של JavaScript שנעשה בהן שימוש כיום על ידי מפתחים. יותר מ-50,000 מקורות שסורקים על ידי HTTPArchive בניידים משתמשים ב-npm, ויש לו כמעט 3 מיליון הורדות שבועיות ב-NPM.

LCP לאתרים של Angular בשנה האחרונה.

בהתאם לדירוגים של המדדים הבסיסיים של חוויית המשתמש, עדיין יש צורך לשפר את האחוז של מקורות Angular שעומדים בערכי הסף של LCP בקטגוריה 'טוב'. רק ל-18.74% מהאתרים ב-Angular היה זמן טעינה ראשוני טוב בנייד ביוני 2022. מכיוון שתמונות הן רכיב ה-LCP ביותר מ-70% מדפי האינטרנט בניידים ובמחשבים, תמונות LCP שלא בוצעה בהן אופטימיזציה יכולות להיות אחת מהסיבות העיקריות לזמן LCP ארוך יותר באתרים של Angular.

הוראה של תמונה ב-Angular נועדה לעזור בשיפור הנתונים האלה.

MVP להנחיה NgOptimizedImage

גרסת ה-MVP של הוראה להצגת תמונות ב-Angular מבוססת על הלקחים שלמדנו מרכיבי התמונות ש-Aurora פיתחה עד היום, תוך התאמת העיצוב לחוויית העיבוד בצד הלקוח של Angular. רבות מהבעיות הסטנדרטיות באופטימיזציה של תמונות טופלו באמצעות:

  • מתן ברירת מחדל חזקה.
  • השלכת שגיאות או אזהרות כדי לוודא תאימות לשיטות המומלצות.

הדגשים העיקריים של העיצוב הם:

  1. טעינה מדורגת חכמה

    מומלץ להשתמש בטעינה באיטרציות לתמונות שאינן גלויות למשתמש בזמן טעינת הדף (לדוגמה, תמונות מתחת לפס התצוגה או תמונות קרוסלה מוסתרות). טעינה איטית מפנה משאבי דפדפן כדי לטעון טקסט, מדיה או סקריפטים קריטיים אחרים. רוב התמונות לא קריטיות וצריך לטעון אותן באיטרציה (lazy loading), אבל רק 7.8% מהדפים השתמשו בטעינה באיטרציה מקומית בשנת 2021.

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

  2. קביעת סדר עדיפויות לתמונות קריטיות

    הוספת רמזים למשאבים (למשל, preload או preconnect) כדי לתת עדיפות לטעינת תמונות קריטיות היא שיטה מומלצת. עם זאת, רוב האפליקציות לא משתמשות בהן. לפי נתוני 'אלמנך האינטרנט לשנת 2021', רק 12.7% מהדפים לנייד משתמשים בטיפים להתחברות מראש, ורק 22.1% מהדפים לנייד משתמשים בטיפים לטעינה מראש.

    ההנחיה לגבי תמונות פועלת בשני מישורים כשתמונות מסומנות כעדיפות.

    • הוא מגדיר את fetchpriority של התמונה ל-"high" כדי שהדפדפן ידע שהוא צריך להוריד את התמונה בעדיפות גבוהה.
    • במצב פיתוח, בדיקה בסביבת זמן הריצה מאשרת שנוספה רמז למשאב preconnect שתואם למקור התמונה.

    במצב פיתוח, ההנחיה משתמשת גם ב-PerformanceObserver API כדי לוודא שתמונת ה-LCP סומנה כ-priority כצפוי. אם הוא לא מסומן כ-priority, תופיע הודעת שגיאה עם הוראה למפתח להוסיף את המאפיין priority לתמונה של LCP.

    בסופו של דבר, השילוב הזה של אוטומציה ותאימות מבטיח שלתמונה של LCP תהיה הנחיה preconnect, ערך המאפיין fetchpriority של high והיא לא תהיה טעינה איטית.

  3. הגדרה אופטימלית לכלי פופולריים לעיבוד תמונות

    מומלץ שאפליקציות Angular ישתמשו ברשתות CDN לתמונות, שרבות מהן מספקות שירותי אופטימיזציה כברירת מחדל.

    ההנחיה מעודדת שימוש ברשתות CDN לתמונות על ידי מתן חוויית פיתוח (DX) מושכת במיוחד להגדרתן באפליקציה. היא תומכת ב-loader API שמאפשר להגדיר את ספק ה-CDN ואת כתובת ה-URL הבסיסית בהגדרה. אחרי שתגדירו את הנכס, תצטרכו רק להגדיר את שם הנכס ב-Markup. לדוגמה,

    // in module providers:
    provideImgixLoader('https://mysite.net/assets/')
    
    // in markup
    <img ngSrc="image.png" >
    <img ngSrc="image2.png" >
    

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

    <img src="https://mysite.net/assets/image.png">
    <img src="https://mysite.net/assets/image2.png">
    

    ההנחיה image מספקת מערכי טעינה מובנים עם הגדרה אופטימלית ל-CDNs הפופולריים ביותר של תמונות. הטענים האלה יפתחו את כתובות ה-URL של התמונות באופן אוטומטי כדי לוודא שנעשה שימוש בפורמט התמונה ובהגדרות הדחיסה המומלצים לכל CDN.

  4. שגיאות ואזהרות מובנות

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

    1. תמונות ללא הגדרת גודל: הוראה של תמונה גורמת לשגיאה אם בסימני ה-HTML של התמונה לא מוגדרים רוחב וגובה מפורשים. תמונות ללא הגדרת גודל עלולות לגרום לשינויי פריסה, שמשפיעים על המדד 'שינוי פריסה מצטבר' (CLS) של הדף. השיטה המומלצת למניעת המצב הזה היא לציין את המאפיינים width ו-height בתמונות.

    2. יחס גובה-רוחב: הוראה של תמונה גורמת להצגת שגיאה כדי להודיע למפתחים אם יחס הגובה-רוחב של width:height שמוגדר ב-HTML לא קרוב ליחס הגובה-רוחב בפועל של התמונה שעבר רינדור. כתוצאה מכך, התמונה עשויה להיראות מעוותת במסך. זה יכול לקרות אם

      1. הגדרתם בטעות את המאפיינים הלא נכונים (רוחב או גובה) או
      2. אם הגדרתם ב-CSS מאפיין אחד של המימדים לפי אחוזים, אבל לא את המאפיין השני (לדוגמה, width: 100% צריך את height: auto כדי להבטיח שהתמונה תגדל בשני המאפיינים).
    3. תמונות גדולות מדי: אם לא מוגדר לתמונה srcset והתמונה המובנית גדולה בהרבה מהתמונה שעבר רינדור, ההנחיה תציג אזהרה עם הצעה להשתמש במאפיינים srcset ו-sizes.

    4. צפיפות תמונה: ההוראה תגרום לשגיאה אם תנסו לכלול ב-srcset תמונה עם צפיפות פיקסלים של יותר מ-3x. בדרך כלל לא מומלץ להשתמש בתוויות שמספרן גבוה מ-2x, כי הדבר עלול לגרום לכך שמכשירים ניידים ברזולוציה גבוהה ייאלצו להוריד תמונות ענקיות. בנוסף, העין האנושית לא יכולה להבחין בהבדל משמעותי מעבר להגדלה של פי 2.

אתגרים

אחד האתגרים העיקריים בתכנון של NgOptimizedImage היה להתאים את אסטרטגיות האופטימיזציה של התמונות לעבודה במסגרת של צד הלקוח. חוויית ברירת המחדל של העיבוד ב-Next.js היא עיבוד בצד השרת (SSR) או יצירת אתר סטטי (SSG), ואילו ב-Angular היא עיבוד בצד הלקוח (CSR). למרות ש-Angular תומכת בספריית SSR – angular/universal – רוב האפליקציות של Angular (כ-60%) משתמשות ב-CSR.

ההנחיה image מיועדת ל-CSR בלבד, כדי להתאים לתרחיש לדוגמה הרגיל באפליקציות Angular. הדבר יצר אילוצים נוספים, והצוות נאלץ לחשוב מחדש איך ליצור אופטימיזציות ספציפיות לאפליקציות CSR.

אלה כמה מהאתגרים שבהם נתקלים:

  1. טיפים לגבי משאבים נתמכים

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

    הוספה ידנית: קשה למפתחים להוסיף את ההצעה לשימוש במשאב preload באופן ידני. ב-Angular נעשה שימוש בקובץ index.html משותף אחד לכל הפרויקט או לכל המסלולים באתר. לכן, הערך של <head> במסמך זהה בכל מסלול (לפחות בזמן ההצגה). הוספת רמז preload ל-<head> תגרום לטעינה מראש של המשאב לכל המסלולים, גם אם הוא לא נדרש. לכן, לא מומלץ להוסיף טיפים ידניים מסוג preload.

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

    בגרסה הראשונה של ההנחיה, שילוב של רמזים מסוג preconnect ורמזים מסוג fetchpriority משמש לקביעת העדיפות של התמונה במקום preload. עם זאת, Aurora עובדת כרגע עם צוות Angular CLI כדי לאפשר הזרקה אוטומטית של רמזים למשאבים בזמן ה-build. כדאי לעקוב אחרי העדכונים.

  2. ביצוע אופטימיזציה של גודל התמונה ופורמט התמונה בשרת

    בדרך כלל, עיבוד של אפליקציות Angular מתבצע בצד הלקוח, ולכן אי אפשר לדחוס תמונות במערכת הקבצים בזמן הבקשה, והן מוצגות כפי שהן. לכן מומלץ להשתמש ב-CDN לתמונות כדי לדחוס תמונות ולהמיר אותן לפורמטים מודרניים כמו WebP או AVIF על פי דרישה.

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

השפעה

הדמו הבא מדגים את ההבדל שיכול להיות להנחיה של תמונה ב-Angular על ביצועי התמונה. הוא משווה בין שני אתרים:

אתר אחד: נעשה בו שימוש ברכיבים מקומיים של <img> עם תמונות שמוצגות דרך Imgix CDN (עם אפשרויות הגדרה שמוגדרות כברירת מחדל).

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

השוואה של פסלים קולנועיים: אתר אחד עם תגי תמונות מקומיים לעומת אתר שני עם הוראה של תמונה ב-Angular.

הצוות עבד עם שותפים כדי לאמת את ההשפעה של הוראה התמונה על הביצועים של אפליקציות Angular אמיתיות לארגונים.

אחד מהשותפים האלה היה Land's End. האתר שלהם היה אמור להיות מקרה לדוגמה טוב לתוצאות שעשויות להופיע באפליקציות אמיתיות.

בדיקות של Lighthouse Lab בוצעו בסביבת בקרת האיכות שלהם לפני ואחרי השימוש בהנחיה לגבי תמונה. במחשב, זמן הטעינה החציוני של התוכן הוויזואלי (LCP) ירד מ-12 שניות ל-3 שניות, שיפור של 75% ב-LCP. בנייד, זמן ה-LCP החציוני ירד מ-20.2 שניות ל-12.0 שניות (שיפור של 40.6%).

מפת הדרכים העתידית

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

  • תמיכה משופרת בתמונות רספונסיביות:

    בשלב זה, הרכיב NgOptimizedImage תומך בשימוש ב-srcset, אבל צריך לספק את המאפיינים srcset ו-sizes באופן ידני לכל תמונה. בעתיד, ההוראה עשויה ליצור את המאפיינים srcset ו-sizes באופן אוטומטי.

  • הזרקה אוטומטית של רמזים למשאבים

    יכול להיות שאפשר לשלב עם Angular CLI כדי ליצור תגים של חיבור מראש וטעינה מראש של תמונות LCP קריטיות.

  • תמיכה ב-Angular SSR

    גרסת ה-MVP תוכננה תוך התחשבות במגבלות של CSR ב-Angular, אבל חשוב גם לבדוק פתרונות לאופטימיזציה של תמונות ל-SSR ב-Angular‏ (angular/universal).

  • שיפורים בחוויית המפתחים

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

    1. תמיכה במצב נוסף (בדומה לאפשרות fill של פריסת תמונה ב-Next.js) שלא מחייב הגדרה מפורשת של רוחב או גובה.
    2. שימוש בשילוב עם CLI כדי להגדיר באופן אוטומטי את הרוחב והגובה של תמונות מקומיות על ידי קביעת המימדים בפועל של התמונה.

סיכום

ההנחיה של Angular לגבי תמונות תהיה זמינה למפתחים בשלבים, החל מגרסה 14.2.0 של תצוגה מקדימה למפתחים. כדאי לנסות את NgOptimizedImage ולשלוח משוב.

תודה מיוחדת לקתי המפניוס (Katie Hempenius) ולאלכס טירת (Alex Castle) על התרומה שלהן.