במאי 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> עם תמונות שמוצגות דרך רשת ה-CDN של Imgix (עם אפשרויות הגדרה שמוגדרות כברירת מחדל).
אתר שני: משתמשים בהנחיה 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) על התרומה שלהן.