במאי 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-cache, ויש לו כמעט 3 מיליון הורדות שבועיות ב-NPM.
בהתאם לדירוגים של המדדים הבסיסיים של חוויית המשתמש, עדיין יש צורך לשפר את אחוז מקורות הנתונים של Angular שעומדים בערכי הסף של LCP בקטגוריה 'טוב'. רק ל-18.74% מהאתרים ב-Angular היה זמן טעינה ראשוני טוב בנייד ביוני 2022. מאחר שתמונות הן רכיב ה-LCP ביותר מ-70% מדפי האינטרנט בניידים ובמחשבים, תמונות LCP שלא בוצעה בהן אופטימיזציה יכולות להיות אחת מהסיבות העיקריות לזמן LCP ארוך יותר באתרים של Angular.
ההנחיה של Angular לתמונות נועדה לעזור בשיפור הנתונים האלה.
MVP להנחיה NgOptimizedImage
גרסת ה-MVP של הוראה להצגת תמונות ב-Angular מבוססת על הלקחים שלמדנו מרכיבי התמונות ש-Aurora פיתחה עד היום, תוך התאמת העיצוב לחוויית העיבוד בצד הלקוח של Angular. רבות מהבעיות הסטנדרטיות באופטימיזציה של תמונות טופלו באמצעות:
- מתן ברירת מחדל חזקה.
- השלכת שגיאות או אזהרות כדי לוודא תאימות לשיטות המומלצות.
הדגשים העיקריים של העיצוב הם:
טעינה מדורגת חכמה
מומלץ להשתמש בטעינה באיטרציות לתמונות שאינן גלויות למשתמש בזמן טעינת הדף (לדוגמה, תמונות מתחת לפס התצוגה או תמונות קרוסלה מוסתרות). טעינה איטית מפנה משאבי דפדפן כדי לטעון טקסט, מדיה או סקריפטים קריטיים אחרים. רוב התמונות לא קריטיות וצריך לטעון אותן באיטרציה (lazy loading), אבל רק 7.8% מהדפים השתמשו בטעינה באיטרציה מקומית בשנת 2021.
כברירת מחדל, ההוראה של Angular לתמונות טוענת בטעינה איטית תמונות לא קריטיות, ומטעינה בטעינה מיידית רק תמונות שסומנו במיוחד בתווית
priority
. כך מובטח שרוב התמונות ייטענו בצורה אופטימלית.קביעת סדר עדיפויות לתמונות קריטיות
הוספת רמזים למשאבים (למשל,
preload
אוpreconnect
) כדי לתת עדיפות לטעינת תמונות קריטיות היא שיטה מומלצת. עם זאת, רוב האפליקציות לא משתמשות בהן. לפי נתוני 'אלמנך האינטרנט לשנת 2021', רק 12.7% מהדפים לנייד משתמשים בטיפים להתחברות מראש, ורק 22.1% מהדפים לנייד משתמשים בטיפים לטעינה מראש.ההנחיה לגבי תמונות פועלת בשני מישורים כשתמונות מסומנות כעדיפות.
- הוא מגדיר את fetchpriority של התמונה לערך
"high"
כדי שהדפדפן ידע שהוא צריך להוריד את התמונה בעדיפות גבוהה. - במצב פיתוח, בדיקה בסביבת זמן הריצה מאשרת שנוספה רמז למשאב
preconnect
שתואם למקור התמונה.
במצב פיתוח, ההנחיה משתמשת גם ב-PerformanceObserver API כדי לוודא שתמונת ה-LCP סומנה כ-
priority
כצפוי. אם הוא לא מסומן כ-priority
, תופיע הודעת שגיאה עם הוראה למפתח להוסיף את המאפייןpriority
לתמונה של LCP.בסופו של דבר, השילוב הזה של אוטומציה ותאימות מבטיח שלתמונה של LCP תהיה הנחיה
preconnect
, ערך המאפייןfetchpriority
שלhigh
והיא לא תתבצע טעינה איטית.- הוא מגדיר את fetchpriority של התמונה לערך
הגדרה אופטימלית לכלי פופולריים לעיבוד תמונות
מומלץ לאפליקציות 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.
שגיאות ואזהרות מובנות
בנוסף לבדיקות האופטימיזציה המובנות שלמעלה, ההנחיה כוללת גם בדיקות מובנות כדי לוודא שהמפתחים פעלו בהתאם לשיטות המומלצות בסימני ה-HTML של התמונות. ההוראה image מבצעת את הבדיקות הבאות.
תמונות ללא הגדרת גודל: הוראה של תמונה גורמת לשגיאה אם בסימני ה-HTML של התמונה לא מוגדרים רוחב וגובה מפורשים. תמונות ללא הגדרת גודל עלולות לגרום לשינויי פריסה, שמשפיעים על המדד 'שינוי פריסה מצטבר' (CLS) של הדף. השיטה המומלצת למניעת המצב הזה היא לציין את המאפיינים
width
ו-height
בתמונות.יחס גובה-רוחב: הוראה של תמונה גורמת להצגת שגיאה כדי להודיע למפתחים אם יחס הגובה-רוחב של
width
:height
שמוגדר ב-HTML לא קרוב ליחס הגובה-רוחב בפועל של התמונה שעבר רינדור. כתוצאה מכך, התמונה עשויה להיראות מעוותת במסך. זה יכול לקרות אם- הגדרתם בטעות את המאפיינים הלא נכונים (רוחב או גובה) או
- אם הגדרתם ב-CSS מאפיין אחד של המימדים לפי אחוזים, אבל לא את המאפיין השני (לדוגמה,
width: 100%
צריך אתheight: auto
כדי להבטיח שהתמונה תגדל בשני המאפיינים).
תמונות גדולות מדי: אם לא מוגדר בתמונה
srcset
והתמונה המובנית גדולה בהרבה מהתמונה שעבר רינדור, ההנחיה תציג אזהרה עם הצעה להשתמש במאפייניםsrcset
ו-sizes
.צפיפות תמונה: ההוראה תגרום להצגת שגיאה אם תנסו לכלול תמונה ב-
srcset
עם צפיפות פיקסלים של יותר מ-3x
. בדרך כלל לא מומלץ להשתמש בתוויות שמספרן גבוה מ-2x
, כי הדבר עלול לגרום לכך שמכשירים ניידים ברזולוציה גבוהה ייאלצו להוריד תמונות ענקיות. בנוסף, העין האנושית לא יכולה להבחין בהבדל משמעותי מעבר להגדלה של פי 2.
אתגרים
אחד האתגרים העיקריים בתכנון של NgOptimizedImage
היה להתאים את אסטרטגיות האופטימיזציה של התמונות לעבודה במסגרת של צד הלקוח. חוויית ברירת המחדל של העיבוד ב-Next.js היא עיבוד בצד השרת (SSR) או יצירת אתר סטטי (SSG), ואילו ב-Angular היא עיבוד בצד הלקוח (CSR). למרות ש-Angular תומכת בספריית SSR – angular/universal – רוב האפליקציות של Angular (כ-60%) משתמשות ב-CSR.
ההנחיה image מיועדת ל-CSR בלבד, כדי להתאים לתרחיש לדוגמה הרגיל באפליקציות Angular. הדבר יצר אילוצים נוספים, והצוות נאלץ לחשוב מחדש איך ליצור אופטימיזציות ספציפיות לאפליקציות CSR.
אלה כמה מהאתגרים שבהם נתקלים:
טיפים לשימוש במשאבים נתמכים
טעינה מראש של נכסים קריטיים עוזרת לדפדפן לגלות אותם מוקדם יותר. עם זאת, קשה לכלול רמזים לגבי משאבים באפליקציות Angular כי:
הוספה ידנית: קשה למפתחים להוסיף את ההצעה לשימוש במשאב
preload
באופן ידני. ב-Angular נעשה שימוש בקובץ index.html משותף אחד לכל הפרויקט או לכל המסלולים באתר. לכן, הערך של<head>
במסמך זהה בכל מסלול (לפחות בזמן ההצגה). הוספת רמזpreload
ל-<head>
תגרום לטעינה מראש של המשאב לכל המסלולים, גם אם הוא לא נדרש. לכן, לא מומלץ להוסיף טיפים ידניים מסוגpreload
.הוספה אוטומטית במהלך העיבוד: לא מועיל להשתמש במסגרת כדי להוסיף רמזים לטעינת נתונים מראש לחלק העליון של המסמך במהלך העיבוד באפליקציית CSR. מאחר שהעיבוד מתבצע אחרי שהקוד של JavaScript מוריד ומופעל, ה-
<head>
יבוצע באיחור רב מכדי שיהיה לו ערך כלשהו.בגרסה הראשונה של ההנחיה, שילוב של רמזים מסוג
preconnect
ורמזים מסוגfetchpriority
משמש לקביעת העדיפות של התמונה במקוםpreload
. עם זאת, Aurora עובדת כרגע עם צוות Angular CLI כדי לאפשר הזרקה אוטומטית של רמזים למשאבים בזמן ה-build. כדאי לעקוב אחרי העדכונים.ביצוע אופטימיזציה של גודל התמונה ופורמט התמונה בשרת
בדרך כלל, עיבוד של אפליקציות Angular מתבצע בצד הלקוח, ולכן אי אפשר לדחוס תמונות במערכת הקבצים בזמן הבקשה, והן מוצגות כפי שהן. לכן מומלץ להשתמש ב-CDN לתמונות כדי לדחוס תמונות ולהמיר אותן לפורמטים מודרניים כמו WebP או AVIF על פי דרישה.
ההנחיה לא אוכפת את השימוש ב-CDN של תמונות, אבל מומלץ מאוד להשתמש בהן עם ההנחיה, והמעמיסים המובנים שלה מבטיחים שייעשה שימוש באפשרויות ההגדרה הנכונות.
השפעה
הדמו הבא מדגים את ההבדל שיכול להיות להנחיה של תמונה ב-Angular על ביצועי התמונה. הוא משווה בין שני אתרים:
אתר אחד: נעשה בו שימוש ברכיבים <img>
מקומיים עם תמונות שמוצגות דרך Imgix CDN (עם אפשרויות הגדרה שמוגדרות כברירת מחדל).
אתר שני: משתמשים בהנחיה image לכל התמונות. הוא כולל גם את פעולות האופטימיזציה המומלצות ישירות על ידי אזהרות או שגיאות שהופקו על ידי ההנחיה.
הצוות עבד עם שותפים כדי לאמת את ההשפעה של הוראה התמונה על הביצועים של אפליקציות 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
לכל תמונה. עם זאת, לציין את הפרטים האלה לכל תמונה עלולה להיות משימה מייגעת עבור חלק מהמפתחים. יש אפשרות לשפר את חוויית המפתחים כאן בגרסה הבאה באופן הבא:- תמיכה במצב נוסף (בדומה לאפשרות
fill
של פריסת תמונה ב-Next.js) שלא מחייב הגדרה מפורשת של רוחב או גובה. - שימוש בשילוב עם CLI כדי להגדיר באופן אוטומטי את הרוחב והגובה של תמונות מקומיות על ידי קביעת המימדים בפועל של התמונה.
- תמיכה במצב נוסף (בדומה לאפשרות
סיכום
ההנחיה של Angular לגבי תמונות תהיה זמינה למפתחים בשלבים, החל מגרסה 14.2.0 של תצוגה מקדימה למפתחים. כדאי לנסות את NgOptimizedImage
ולשלוח משוב.
תודה מיוחדת לקתי המפניס (Katie Hempenius) ולאלכס טירת (Alex Castle) על התרומה שלהן.