בניית מודעות לאינטרנט מעניקה לך פוטנציאל חשיפה ללא תחרות. אפליקציית האינטרנט נמצאת במרחק לחיצה וזמינה כמעט בכל מכשיר מחובר: סמארטפון, טאבלט, מחשב נייד ומחשב, טלוויזיה, ועוד, ללא קשר למותג או לפלטפורמה. כדי לספק את חוויית השימוש הטובה ביותר, יצרתם אתר רספונסיבי שמתאים את המראה והפונקציונליות לכל גורם צורה. עכשיו אתם בודקים את רשימת המשימות לביצוע אופטימיזציה של הביצועים כדי לוודא שהאפליקציה נטענת מהר ככל האפשר: ביצעתם אופטימיזציה של נתיב העיבוד הקריטי, דחסתם את משאבי הטקסט ושמרתם אותם במטמון, ועכשיו אתם בודקים את משאבי התמונות, שבדרך כלל אחראים לרוב הבייטים המועברים. הבעיה היא שאופטימיזציה של תמונות קשה:
- קובעים את הפורמט המתאים (וקטור לעומת רשת נקודות)
- קביעת הפורמטים האופטימליים לקידוד (jpeg, webp וכו')
- איך קובעים את הגדרות הדחיסה המתאימות (דחיסה עם אובדן נתונים לעומת דחיסה ללא אובדן נתונים)
- קביעת אילו מטא-נתונים צריך לשמור או להסיר
- ליצור כמה וריאנטים של כל אחד מהם לכל מסך ורזולוציית DPR
- ...
- התחשבות בסוג הרשת, במהירות ובהעדפות של המשתמש
כל אחת מהבעיות האלה היא בעיה מוכרת. ביחד, הם יוצרים מרחב אופטימיזציה גדול שאנחנו (המפתחים) לעיתים קרובות מתעלמים ממנו או מזניחים אותו. בני אדם לא מצליחים לחקור שוב ושוב את אותו מרחב חיפוש, במיוחד כשיש הרבה שלבים. לעומת זאת, מחשבים מצטיינים במשימות מהסוג הזה.
התשובה לשיטת אופטימיזציה טובה ומתמשכת לתמונות ולמשאבים אחרים עם מאפיינים דומים היא פשוטה: אוטומציה. אם אתם משנים את ההגדרות של המשאבים באופן ידני, אתם עושים זאת בצורה שגויה: אתם תשכחו, תתעצלו או שמישהו אחר יעשה את הטעות הזו בשבילכם – מובטח.
הסאגה של המפתח שמתחשב בביצועים
החיפוש במרחב של אופטימיזציית תמונות מתבצע בשני שלבים נפרדים: בשלב ה-build ובזמן הריצה.
- חלק מהאופטימיזציות הן ייחודיות למשאב עצמו – למשל, בחירת הפורמט וסוג הקידוד המתאימים, שינוי הגדרות הדחיסה לכל מקודד, הסרת מטא-נתונים מיותרים וכו'. אפשר לבצע את השלבים האלה בזמן ה-build.
- אופטימיזציות אחרות נקבעות לפי הסוג והמאפיינים של הלקוח שמבקש אותן, וצריך לבצע אותן בזמן הריצה: בחירת המשאב המתאים ל-DPR ולרוחב התצוגה המיועד של הלקוח, תוך התחשבות במהירות הרשת של הלקוח, בהעדפות של המשתמש והאפליקציה וכו'.
הכלים שקשורים לזמן ה-build קיימים, אבל אפשר לשפר אותם. לדוגמה, אפשר לחסוך הרבה כסף על ידי כוונון דינמי של ההגדרה 'איכות' לכל תמונה לכל פורמט תמונה, אבל עדיין לא ראיתי אף אחד שמשתמש בהגדרה הזו בפועל מחוץ למחקר. זהו תחום מתאים לחדשנות, אבל למטרות הפוסט הזה נשאיר אותו כאן. נתמקד בחלק של הסיפור שקשור לזמן הריצה.
<img src="/image/thing" sizes="50vw"
alt="image thing displayed at 50% of viewport width">
הכוונה של האפליקציה פשוטה מאוד: לאחזר את התמונה ולהציג אותה ב-50% מחלון התצוגה של המשתמש. זה המקום שבו רוב המעצבים שוטפים את הידיים וממשיכים לבר. בינתיים, מפתחי הצוות שמודעים לביצועים נמצאים פה למשך לילה שלם:
- כדי לקבל את הדחיסה הטובה ביותר, היא רוצה להשתמש בפורמט התמונה האופטימלי לכל לקוח: WebP ל-Chrome, JPEG XR ל-Edge ו-JPEG לשאר.
- כדי לקבל את האיכות החזותית הטובה ביותר, היא צריכה ליצור כמה וריאנטים לכל תמונה ברזולוציות שונות: 1x, 1.5x, 2x, 2.5x, 3x ואולי אפילו כמה בין תמונות.
- כדי להימנע מהוספת פיקסלים מיותרים, היא צריכה להבין מה המשמעות של "50% מאזור התצוגה של המשתמש" – יש הרבה רוחבי תצוגה שונים!
- באופן אידיאלי, היא רוצה גם לספק חוויה עמידה שבה משתמשים ברשתות איטיות יותר יאחזרו באופן אוטומטי רזולוציה נמוכה יותר. אחרי הכול, הכול תלוי בזמן הזכוכית.
- האפליקציה גם חושפת כמה אמצעי בקרה של משתמשים שמשפיעים על משאב התמונה שצריך לאחזר, כך שצריך להביא בחשבון גם את הגורם הזה.
אה, ואז המעצבת מבינה שהיא צריכה להציג תמונה אחרת
ברוחב של 100% אם אזור התצוגה קטן כדי שיהיה קל יותר לקרוא אותה. כלומר, עכשיו צריך לחזור על אותו תהליך לנכס נוסף, ואז להגדיר את האחזור כמותנה לפי גודל חלון התצוגה. הזכרתי שהדברים האלה קשים? אוקיי, בואו נתחיל. הרכיב picture
יעזור לנו להגיע רחוק:
<picture>
<!-- serve WebP to Chrome and Opera -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
/image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
/image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
type="image/webp">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
/image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
/image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
type="image/webp">
<!-- serve JPEGXR to Edge -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.jpgxr 200w, /image/thing-400.jpgxr 400w,
/image/thing-800.jpgxr 800w, /image/thing-1200.jpgxr 1200w,
/image/thing-1600.jpgxr 1600w, /image/thing-2000.jpgxr 2000w"
type="image/vnd.ms-photo">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.jpgxr 200w, /image/thing-crop-400.jpgxr 400w,
/image/thing-crop-800.jpgxr 800w, /image/thing-crop-1200.jpgxr 1200w,
/image/thing-crop-1600.jpgxr 1600w, /image/thing-crop-2000.jpgxr 2000w"
type="image/vnd.ms-photo">
<!-- serve JPEG to others -->
<source
media="(min-width: 50em)"
sizes="50vw"
srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
/image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
/image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
<source
sizes="(min-width: 30em) 100vw"
srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
/image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
/image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
<!-- fallback for browsers that don't support picture -->
<img src="/image/thing.jpg" width="50%">
</picture>
טיפלנו בכיוון הגרפיקה ובבחירת הפורמט והצגנו שש וריאנטים של כל תמונה כדי להביא בחשבון את השונות ב-DPR ורוחב התצוגה של המכשיר של הלקוח. מרשים!
לצערנו, הרכיב picture
לא מאפשר לנו להגדיר כללים לאופן שבו הוא צריך להתנהג בהתאם לסוג החיבור או למהירות שלו. עם זאת, אלגוריתם העיבוד שלו מאפשר לסוכנות המשתמשים לשנות את המשאב שאיתו היא מאחזרת במקרים מסוימים – ראו שלב 5. נצטרך רק לקוות שסוכן המשתמש מספיק חכם. (הערה: אף אחת מההטמעות הנוכחיות לא עומדת בדרישות האלה). באופן דומה, אין הוקים (hooks) ברכיב picture
שמאפשרים לוגיקה ספציפית לאפליקציה שמשקללת לפי העדפות האפליקציה או המשתמשים. כדי לקבל את שני הביטים האחרונים, נצטרך להעביר את כל הלוגיקה שלמעלה ל-JavaScript, אבל הפעולה הזו מוותרת על האופטימיזציות של סורק הטעינה מראש שמוצעות על ידי picture
. הממ…
מלבד המגבלות האלה, הפתרון הזה עובד. טוב, לפחות לגבי הנכס הספציפי הזה. האתגר האמיתי וארוך הטווח כאן הוא שאנחנו לא מצפים מהמעצב או מהמפתח ליצור באופן ידני קוד כזה לכל נכס. זוהי חידת מוח מהנה בניסיון הראשון, אבל היא מאבדת את האטרקטיביות שלה מיד אחרי כן. אנחנו זקוקים לאוטומציה. אולי אפשר להשתמש בכלי של סביבת הפיתוח המשולבת או בכלי אחר לטרנספורמציה של תוכן כדי ליצור את הקוד הסטנדרטי שלמעלה באופן אוטומטי.
אוטומציה של בחירת המשאבים באמצעות רמזים מהלקוח
נושמים עמוק, משהים את חוסר האמון ומעיינים בדוגמה הבאה:
<meta http-equiv="Accept-CH" content="DPR, Viewport-Width, Width">
...
<picture>
<source media="(min-width: 50em)" sizes="50vw" srcset="/image/thing">
<img sizes="100vw" src="/image/thing-crop">
</picture>
תאמינו או לא, הדוגמה שלמעלה מספיקה כדי לספק את כל היכולות שמוצעות כמו תגי העיצוב הארוכים יותר של התמונות שלמעלה, וכמו שנראה, היא גם מספקת למפתחים שליטה מלאה על האופן, הזמן והשליפה של משאבי התמונות. 'הקסם' נמצא בשורה הראשונה, שמאפשרת דיווח על רמזים מהלקוח ומורה לדפדפן לפרסם לשרת את יחס הפיקסלים של המכשיר (DPR
), את רוחב אזור התצוגה של הפריסה (Viewport-Width
) ואת רוחב התצוגה המיועד (Width
) של המשאבים.
כשהרמזים על הלקוח מופעלים, התגים שנוצרים בצד הלקוח שומרים רק על דרישות התצוגה. המעצב לא צריך לדאוג לגבי סוגי תמונות, רזולוציות של לקוחות, נקודות עצירה אופטימליות כדי לצמצם את מספר הבייטים שנשלחים או קריטריונים אחרים לבחירת משאבים. בואו נודה בזה, הם אף פעם לא עשו זאת, וגם לא צריכים. יתרון נוסף הוא שהמפתח לא צריך גם לכתוב מחדש ולהרחיב את ה-Markup שלמעלה, כי בחירת המשאב בפועל מתבצעת במשא ומתן בין הלקוח לשרת.
Chrome 46 כולל תמיכה מובנית ברמזים של DPR
, Width
ו-Viewport-Width
. ההצעות מושבתות כברירת מחדל, והקוד <meta http-equiv="Accept-CH" content="...">
שלמעלה משמש כאות להצטרפות שמורה ל-Chrome לצרף את הכותרות שצוינו לבקשות היוצאות. עכשיו נבחן את הכותרות של הבקשה והתגובה לבקשת תמונה לדוגמה:
Chrome מכריז על התמיכה שלו בפורמט WebP דרך כותרת הבקשה Accept. בדומה, דפדפן Edge החדש מכריז על תמיכה ב-JPEG XR דרך כותרת הבקשה Accept.
שלוש הכותרות הבאות של הבקשות הן כותרות עם הרמז ללקוח, שמפרסמות את יחס הפיקסלים של המכשיר של המכשיר של הלקוח (3x), רוחב אזור התצוגה של הפריסה (460 פיקסלים) ורוחב התצוגה המיועד של המשאב (230 פיקסלים). כך אפשר לספק לשרת את כל המידע הנדרש כדי לבחור את וריאציית התמונה האופטימלית על סמך כללי המדיניות שלה: זמינות המשאבים שנוצרו מראש, עלות הקידוד מחדש או שינוי הגודל של משאב, מידת הפופולריות של משאב, העומס הנוכחי בשרת וכן הלאה. במקרה הזה, השרת משתמש בהצעות DPR
ו-Width
ומחזיר משאב WebP, כפי שמצוין בכותרות Content-Type
, Content-DPR
ו-Vary
.
אין כאן קסם. העברנו את בחירת המשאבים מתיוג HTML לתוך המשא ומתן של בקשה-תגובה בין הלקוח לשרת. כתוצאה מכך, ה-HTML מתייחס רק לדרישות התצוגה, וכל מעצב ומפתח יכול לכתוב אותו. לעומת זאת, החיפוש במרחב של אופטימיזציית התמונות מועבר למחשבים, ועכשיו אפשר לבצע אותו בקלות באופן אוטומטי בקנה מידה נרחב. זכורים לכם המפתחים שחשוב להם לשפר את הביצועים? עכשיו היא צריכה לכתוב שירות תמונות שיכול למנף את הרמזים שסופקו ומחזירה את התשובה המתאימה: היא יכולה להשתמש בכל שפה או שרת שהיא אוהבת, או לאפשר לשירות צד שלישי או לרשת CDN לעשות זאת בשמה.
<img src="/image/thing" sizes="50vw"
alt="image thing displayed at 50% of viewport width">
בנוסף, זוכרים את הבחור שלמעלה? עם אותות הלקוח, תג התמונה הצנועה עכשיו מותאם ל-DPR, לאזור התצוגה ולרוחב, ללא תגי עיצוב נוספים. אם אתם צריכים להוסיף כיוון אומנותי, אתם יכולים להשתמש בתג picture
כמו שציינו למעלה. אחרת, כל תגי התמונה הקיימים הפכו לחכמים הרבה יותר. רמזים ללקוח משפרים את הרכיבים הקיימים img
ו-picture
.
שליטה על בחירת המשאבים באמצעות Service Worker
למעשה, ServiceWorker הוא שרת proxy בצד הלקוח שפועל בדפדפן שלך. הוא מיירט את כל הבקשות היוצאות ומאפשר לבדוק, לשכתב, לשמור במטמון ואפילו לסנתז תשובות. זה נכון גם לגבי תמונות. כשהטיפים ללקוח מופעלים, ה-ServiceWorker הפעיל יכול לזהות את הבקשות לתמונות, לבדוק את הטיפים ללקוח שסופקו ולהגדיר את לוגיקת העיבוד שלו.
self.onfetch = function(event) {
var req = event.request.clone();
console.log("SW received request for: " + req.url)
for (var entry of req.headers.entries()) {
console.log("\t" + entry[0] +": " + entry[1])
}
...
}
ServiceWorker נותן לכם שליטה מלאה בצד הלקוח על בחירת המשאבים. זה קריטי. חשוב להבין את זה, כי האפשרויות הן כמעט אינסופיות:
- אפשר לשכתב את ערכי הכותרות של רמזים על הלקוח שהוגדרו על ידי סוכן המשתמש.
- אפשר לצרף לבקשה ערכים חדשים של כותרות של רמזים ללקוח.
- אפשר לשכתב את כתובת ה-URL ולהפנות את בקשת התמונה לשרת חלופי (למשל CDN).
- אפשר אפילו להעביר את ערכי ההנחיה מהכותרות לכתובת ה-URL עצמה, אם זה מקל על הפריסה בתשתית.
- אפשר לשמור תשובות במטמון ולהגדיר לוגיקה משלכם לגבי המשאבים שיוצגו.
- אפשר לשנות את התגובה על סמך הקישוריות של המשתמשים.
- אתם יכולים להשתמש ב-NetInfo API כדי לשלוח לשרת שאילתה לגבי ההעדפות שלכם ולהציג אותן.
- אם האחזור איטי, אפשר להחזיר תשובה חלופית.
- אפשר להביא בחשבון שינויים בהעדפות של האפליקציה והמשתמשים.
- אתם יכולים לעשות כל מה שאתם רוצים.
הרכיב picture
מספק את השליטה הנדרשת בכיוון הצילום של הסרטון בתגי העיצוב של HTML.
הטיפים ללקוח מספקים הערות לבקשות התמונות שמתקבלות, שמאפשרות אוטומציה של בחירת המשאבים. ServiceWorker מספק יכולות לניהול בקשות ותשובות בצד הלקוח. זהו האינטרנט המורחב בפעולה.
שאלות נפוצות על רמזים על הלקוח
איפה אפשר להשתמש בטיפים לגבי לקוחות? התכונה הזו נוספה ב-Chrome 46. בבדיקה ב-Firefox וב-Edge.
למה צריך להביע הסכמה לשימוש בטיפים ללקוח? אנחנו רוצים למזער את התקורה של אתרים שלא ישתמשו בטיפים ללקוח. כדי להפעיל רמזים ללקוח, צריך לספק באתר את הכותרת
Accept-CH
או הוראה<meta http-equiv>
מקבילה בסימון הדף. אם אחד מהם קיים, סוכן המשתמש יתווסף את הטיפים המתאימים לכל הבקשות של משאבי המשנה. בעתיד, יכול להיות שנציע מנגנון נוסף לשמירת ההעדפה הזו למקור מסוים, שיאפשר לשלוח את אותן הנחיות לבקשות ניווט.למה אנחנו צריכים רמזים ללקוח אם יש לנו ServiceWorker? ל-ServiceWorker אין גישה למידע על הפריסה, המשאבים ורוחבו של אזור התצוגה. לכל הפחות, לא בלי לכלול פעולות הלוך ושוב יקרות ועיכוב משמעותי של בקשת התמונה, למשל כשבקשה לתמונה נשלחה ביוזמת המנתח של הטעינה מראש. הטיפים ללקוח משולבים בדפדפן כדי שהנתונים האלה יהיו זמינים כחלק מהבקשה.
האם טיפים ללקוח מיועדים למשאבי תמונות בלבד? התרחיש העיקרי לשימוש ב-DPR, ב-Viewport-Width וברמזים לגבי רוחב הוא לאפשר בחירת משאבים לנכסי תמונה. עם זאת, אותן הנחיות מועברות לכל המשאבים המשניים, ללא קשר לסוג שלהם. לדוגמה, גם לבקשות CSS ו-JavaScript מגיע אותו מידע, ואפשר להשתמש בהן כדי לבצע אופטימיזציה של המשאבים האלה.
בקשות מסוימות לתמונות לא מדווחות על רוחב. למה? יכול להיות שהדפדפן לא יידע מהו רוחב התצוגה המיועד כי האתר מסתמך על הגודל הפנימי של התמונה. כתוצאה מכך, ההצעה לגבי הרוחב לא תופיע בבקשות כאלה, ובבקשות שאין להן 'רוחב תצוגה' – למשל, משאב JavaScript. כדי לקבל רמזים לגבי רוחב, חשוב לציין ערך של sizes בתמונות.
מה לגבי <insert my favorite hint>? ServiceWorker מאפשר למפתחים ליירט ולשנות (למשל, להוסיף כותרות חדשות) את כל הבקשות היוצאות. לדוגמה, קל להוסיף מידע שמבוסס על NetInfo כדי לציין את סוג החיבור הנוכחי. אפשר לקרוא מידע נוסף במאמר דיווח על יכולות באמצעות ServiceWorker. ההנחיות 'הילידים' שנשלחות ב-Chrome (DPR, Width, Resource-Width) מוטמעות בדפדפן כי הטמעה טהורה שמבוססת על תוכנה תעכב את כל בקשות התמונות.
איפה אפשר לקבל מידע נוסף, לראות הדגמות נוספות ומה עוד? מומלץ לעיין במסמך ההסבר, ואפשר לפתוח פנייה בנושא ב-GitHub אם יש לכם משוב או שאלות נוספות.