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

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

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

כדי לספק חוויית משתמש טובה, מומלץ שתכונת האינטראקציה עד לצבע הבא תהיה 200 אלפיות השנייה לכל היותר. כדי לוודא שאתם עומדים ביעד הזה עבור רוב המשתמשים, סף טוב למדידת ביצועים הוא האחוזון ה-75 של טעינות דפים, המפולח במכשירים ניידים ובמחשבים.

ערכי INP טובים הם 200 אלפיות שנייה או פחות, ערכים נמוכים הם יותר מ-500 אלפיות שנייה, וכל מה שביניהם צריך לשפר.

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

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

מה גורם ל-INP נמוך

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

איתור אינטראקציות איטיות בשדה

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

אם אתם לא מסתמכים על ספק RUM כדי לקבל נתוני שדות, המדריך לנתוני שדות INP מייעץ להשתמש בדוח חוויית המשתמש ב-Chrome (CrUX) דרך PageSpeed Insights כדי למלא את הפערים האלה. CrUX הוא מערך הנתונים הרשמי של תוכנית Core Web Vitals, והוא מספק סיכום ברמה גבוהה של מדדים לגבי מיליוני אתרים, כולל INP. עם זאת, לעיתים קרובות CrUX לא מספקת את הנתונים ההקשריים שמקבלים מספק RUM כדי לעזור לכם לנתח בעיות. לכן, עדיין מומלץ שאתרים ישתמשו בספק RUM כשהדבר אפשרי, או יטמיעו פתרון RUM משלהם כתוספת למה שזמין ב-CrUX.

לאבחן אינטראקציות איטיות במעבדה

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

אופטימיזציה של האינטראקציות

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

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

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

זיהוי וצמצום של עיכוב קלט

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

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

הקשר בין הערכת סקריפטים למשימות ארוכות במהלך ההפעלה

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

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

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

אופטימיזציה של קריאה חוזרת (callbacks) של אירוע

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

תפוקה ל-thread הראשי לעיתים קרובות

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

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

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

עדיף לתת תפוקה ללא הבחנה מאשר לא להניב כלל. עם זאת, יש דרך מורכבת יותר להגיע ל-thread הראשי דורשת זמן של קריאה חוזרת רק אחרי קריאה חוזרת (callback) של אירוע, שמעדכנת את ממשק המשתמש, כך שלוגיקת העיבוד תוכל לפעול מוקדם יותר.

מתן תפוקה כדי לאפשר עבודת רינדור מוקדם יותר

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

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

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

  1. מעדכנים בתיבת הטקסט את מה שהמשתמש הקליד ומחילים כל עיצוב נדרש.
  2. עדכון החלק בממשק המשתמש שבו מוצגת ספירת המילים הנוכחית.
  3. מפעילים לוגיקה כדי לבדוק אם יש שגיאות איות.
  4. שומרים את השינויים האחרונים (באופן מקומי או במסד נתונים מרוחק).

הקוד לשם כך עשוי להיראות כך:

textBox.addEventListener('input', (inputEvent) => {
  // Update the UI immediately, so the changes the user made
  // are visible as soon as the next frame is presented.
  updateTextBox(inputEvent);

  // Use `setTimeout` to defer all other work until at least the next
  // frame by queuing a task in a `requestAnimationFrame()` callback.
  requestAnimationFrame(() => {
    setTimeout(() => {
      const text = textBox.textContent;
      updateWordCount(text);
      checkSpelling(text);
      saveChanges(text);
    }, 0);
  });
});

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

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

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

נמנעים מעומס פריסות

עומס פריסות של פריסות, שלפעמים נקרא 'פריסה סנכרונית מאולצת', הוא בעיה בביצועי העיבוד, שבה הפריסה מתרחשת באופן סינכרוני. הוא מתרחש כשמעדכנים סגנונות ב-JavaScript ולאחר מכן קוראים אותם באותה משימה — ויש מאפיינים רבים ב-JavaScript שיכולים לגרום לרשינג (thrashing) פריסה.

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

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

מזער את ההשהיה בהצגה

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

מזעור ה-DOM

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

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

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

אפשר להשתמש ב-content-visibility כדי לעבד באופן מדורג רכיבים שלא מופיעים במסך

אחת הדרכים שבהן אפשר להגביל את כמות עבודת הרינדור במהלך טעינת הדף ועבודת הרינדור בתגובה לאינטראקציות של משתמשים היא להשתמש במאפיין content-visibility של CSS, שלמעשה נחשב לרינדור באופן מדורג של רכיבים כשהם מתקרבים לאזור התצוגה. כדאי לתרגל קצת את השימוש ב-content-visibility כדי שיהיה יעיל, אבל כדאי לבדוק אם התוצאה היא זמן רינדור קצר יותר שיכול לשפר את ה-INP של הדף.

שימו לב לעלויות הביצועים של עיבוד HTML באמצעות JavaScript

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

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

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

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

סיכום

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

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

תמונה ראשית (Hero) מ-Unbounce, מאת David Pisnoy, שהשתנתה בהתאם לרישיון של Un פעילות.