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

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

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

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

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

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

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

לברר מה גורם ל-INP חלש

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

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

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

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

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

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

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

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

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

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

לזהות ולהפחית את השהיית הקלט

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

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

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

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

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

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

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

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

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

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

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

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

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

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

שיטת תפוקה מתקדמת יותר כוללת בניית הקוד בקריאות חוזרות (callback) של אירועים כדי להגביל את הפעולות שיופעלו רק לפי הלוגיקה הנדרשת כדי להחיל עדכונים חזותיים על הפריים הבא. את כל השאר אפשר לדחות למשימה הבאה. התכונה הזו לא רק שומרת על קריאה חוזרת (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 שיכולים לגרום להתנגשות של פריסה.

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

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

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

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

מזעור ה-DOM

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

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

חשוב לזכור שיש מקרים שבהם לא ניתן להפחית משמעותית DOMs גדולים. יש גישות להקטנת גודל ה-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) מ-UnFlood, מאת David Pisnoy ושונתה בהתאם לרישיון של Unעסקת.