אוטומציה של דחיסה וקידוד

יצירת מקורות תמונות עם ביצועים גבוהים היא חלק חלק מתהליך הפיתוח שלך.

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

חלון אוטומטי לקידוד תמונות.

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

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

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

אוטומציה של דחיסה וקידוד

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

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

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

בכל הנוגע לעיבוד עצמו, יש מספר עצום של ספריות קוד פתוח לעיבוד תמונות שמספקות שיטות להמרה, שינוי ועריכה של תמונות באצוות, שמתחרות על המהירות, היעילות והאמינות. ספריות העיבוד יאפשרו לכם להחיל הגדרות קידוד ודחיסה על ספריות שלמות של תמונות בבת אחת, ללא צורך לפתוח תוכנה לעריכת תמונות, ובצורה שמשמרת את מקורות התמונות המקוריים במקרה שיהיה צורך לשנות את ההגדרות באופן מיידי. הם מיועדים לפעול במגוון הקשרים, החל מסביבת הפיתוח המקומית ועד לשרת האינטרנט עצמו. לדוגמה, אפשר להרחיב את ImageMin עבור Node.js, שמתמקד בדחיסת נתונים, כך שיתאימו לאפליקציות ספציפיות באמצעות מגוון יישומי פלאגין. לעומת זאת, הפלטפורמות ImageMagick ו-Sharp של Node.js מגיעות עם מספר מפתיע של תכונות.

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

תהליכי עבודה וכלים מקומיים לפיתוח

תוכלו להשתמש בתוכנות להרצת משימות ובחבילות כמו Gunt, Gulp או Webpack כדי לבצע אופטימיזציה של נכסי תמונות, לצד משימות נפוצות אחרות שקשורות לביצועים, כמו הקטנה של CSS ו-JavaScript. נמחיש את זה באמצעות תרחיש פשוט יחסית: ספרייה בפרויקט מכילה תריסר תמונות מצולמות, שמיועדות לשימוש באתר ציבורי.

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

אמנם באמצעות תוכנה לעריכת תמונות מדובר במשימה שחוזרת על עצמה וגוזלת זמן רב, אבל הרצת משימות כמו Gulp יכולה לבצע חזרה אוטומטית מהסוג הזה. הפלאגין gulp-responsive, שמשתמש ב-Sharp, הוא אפשרות אחת מתוך רבות שפועלות לפי תבנית דומה: איסוף של כל הקבצים בספריית מקור, קודד מחדש ודחיסה שלהם על סמך אותו קיצור דרך סטנדרטי של 'איכות' שלמדתם עליו פורמטים של תמונות ודחיסות. לאחר מכן, הקבצים שיתקבלו ישויכו לנתיב שתגדירו. תוכלו להפנות אותם במאפייני src של רכיבי img שגלויים למשתמשים והשארת את הקבצים המקוריים ללא שינוי.

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

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

כדי להפיק פלט של כמה קבצים, צריך להעביר אובייקטים מרובים של הגדרות אישיות, מלבד הוספה של מפתח width וערך בפיקסלים:

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
    .pipe(respimg({
    '*': [{
            width: 1000,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-1000' }
            },
            {
            width: 800,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-800' }
            },
            {
            width: 400,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-400' },
        }]
        })
    )
    .pipe(dest('./img/'));
}

במקרה של הדוגמה שלמעלה, התמונה המקורית (monarch.png) הייתה גדולה מ-3.3MB. גודל הקובץ הגדול ביותר שנוצר על ידי המשימה הזו (monarch-1000.jpeg) הוא בערך 150KB. גודל הקובץ הקטן ביותר, monarch-400.web, הוא 32KB בלבד.

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

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

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

שימוש בתגי עיצוב של תמונות רספונסיביות

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

srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w"

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

כפי שלמדתם בתמונות רספונסיביות, כדאי להשתמש באלמנט <picture> כדי לטפל בצורה חלקה בדפוס החלופה של WebP או JPEG. במקרה כזה, ייעשה שימוש במאפיין type ביחד עם srcset.

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

כמו שגילית, דפדפנים שתומכים ב-WebP יזהו את התוכן של המאפיין type ויבחרו במאפיין srcset של רכיב <source> הזה כרשימת המועמדים לתמונות. דפדפנים שלא מזהים את image/webp כסוג מדיה חוקי יתעלמו מה<source> הזה, וישתמשו במאפיין srcset של הרכיב <img> הפנימי.

יש שיקול נוסף אחד מבחינת התמיכה בדפדפנים: דפדפנים שאין בהם תמיכה בתגי עיצוב של תמונות רספונסיביות עדיין יצטרכו להשתמש בחלופה, או שאנחנו עלולים להסתכן בתמונה קטועה בהקשרים ישנים במיוחד של גלישה. מכיוון שהדפדפנים האלה מתעלמים מ-<picture>, <source> ומ-srcset, לכן כדאי לציין מקור ברירת מחדל במאפיין src של <img> הפנימי.

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

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

ההתמודדות עם sizes יכולה להיות קצת יותר קשה. כמו שהבנתם, הפרמטר sizes הוא למעשה לפי הקשר – אי אפשר לאכלס את המאפיין בלי לדעת מה גודל המקום שאליו התמונה אמורה לתפוס את הפריסה בפריסה המעובדת. כדי לקבל את הבקשות היעילות ביותר, צריך לכלול מאפיין sizes מדויק בתגי העיצוב שלו בזמן שמשתמש הקצה שולח את הבקשות האלה, הרבה לפני הבקשה לסגנונות ששולטים בפריסת הדף. השמטת הערך sizes באופן מלא אינה רק הפרה של מפרט ה-HTML, אלא גם תגרום להתנהגות ברירת המחדל שמקבילה ל-sizes="100vw". כך, המערכת תודיע לדפדפן שהתמונה הזו מוגבלת רק על ידי אזור התצוגה עצמו, וכתוצאה מכך ייבחרו מקורות המועמדים הגדולים ביותר האפשריים.

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

דוח תמונות רספונסיביות שמציג אי-התאמה בגודל/רוחב.

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

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

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

כמובן שאם אתם כבר פועלים לפי מסגרת עיבוד בצד הלקוח, כמו React או Vue, מדובר בחוב שכבר תצברו. במקרים כאלה, אם תשתמשו ב-Lazysizes, תוכלו להעלים את מאפייני sizes כמעט לחלוטין. יותר מכך: מאחר ש-sizes="auto" בתמונות בטעינה מדורגת צובר קונצנזוס והטמעות מותאמות, פלטפורמות Lazysizes הופכות ל-Polyfill של התנהגות דפדפן סטנדרטית חדשה.