איך מבצעים אופטימיזציה של מדד 'מאינטראקציה ועד הצגת התגובה' באתר
פורסם: 19 במאי 2023, עדכון אחרון: 2 בספטמבר 2025
מהירות התגובה לאינטראקציה באתר (INP) הוא מדד יציב של מדדים בסיסיים של חוויית המשתמש. המדד הזה מעריך את רמת הרספונסיביות הכוללת של דף לאינטראקציות של משתמשים, על ידי מעקב אחר זמן האחזור של כל האינטראקציות שעומדות בדרישות שמתרחשות במהלך משך החיים של הביקור של המשתמש בדף. ערך ה-INP הסופי הוא משך הזמן הארוך ביותר של אינטראקציה שזוהה (לפעמים ללא חריגים חשודי טעות).
כדי לספק חוויית משתמש טובה, אתרי אינטרנט צריכים לשאוף להשיג מדד INP של 200 אלפיות השנייה או פחות. כדי לעמוד ביעד הזה עבור רוב המשתמשים, ערך הסף המומלץ למדידה הוא האחוזון ה-75 של טעינות הדפים. כדאי לנתח את הנתונים האלה במחשבים ובמכשירים ניידים בנפרד.
יכול להיות שיהיו באתר מעט אינטראקציות או שלא יהיו בכלל – למשל, דפים שכוללים בעיקר טקסט ותמונות, עם מעט רכיבים אינטראקטיביים או ללא רכיבים כאלה. לחלופין, באתרים כמו עורכי טקסט או משחקים, יכולות להיות מאות אינטראקציות, ואפילו אלפים. בכל מקרה, אם ערך ה-INP גבוה, חוויית המשתמש עלולה להיפגע.
שיפור מדד INP דורש זמן ומאמץ, אבל התגמול הוא חוויית משתמש טובה יותר. במדריך הזה נסביר איך לשפר את מדד INP.
איך מבררים מה גורם לערך INP נמוך
כדי לפתור בעיות שקשורות לאינטראקציות איטיות, צריך נתונים שיצביעו על כך שערך ה-INP של האתר נמוך או שצריך לשפר אותו. אחרי שתקבלו את המידע הזה, תוכלו לעבור למעבדה כדי להתחיל לאבחן אינטראקציות איטיות ולמצוא פתרון.
איתור אינטראקציות איטיות בשטח
מומלץ להתחיל את תהליך האופטימיזציה של INP עם נתונים מהשטח. במקרה הטוב, נתונים מהשטח מספק Real User Monitoring (RUM) יציגו לכם לא רק את ערך ה-INP של הדף, אלא גם נתונים הקשריים שמדגישים איזו אינטראקציה ספציפית אחראית לערך ה-INP עצמו, אם האינטראקציה התרחשה במהלך טעינת הדף או אחריה, סוג האינטראקציה (קליק, הקשה על מקש או הקשה על המסך) ומידע חשוב אחר.
אם אתם לא מסתמכים על ספק RUM כדי לקבל נתוני שטח, המדריך לנתוני שטח של INP ממליץ להשתמש ב-PageSpeed Insights כדי לראות את הנתונים של הדוח לגבי חוויית המשתמש ב-Chrome (CrUX) ולמלא את הפערים. CrUX הוא מערך הנתונים של Google שמשמש לתוכנית Core Web Vitals, והוא מספק סיכום ברמה גבוהה של מדדים למיליוני אתרים, כולל INP. עם זאת, ב-CrUX לרוב לא מוצגים נתונים הקשריים כמו אלה שמתקבלים מספק RUM, שיכולים לעזור לכם לנתח בעיות. לכן, אנחנו עדיין ממליצים לאתרים להשתמש בספק RUM כשאפשר, או להטמיע פתרון RUM משלהם כדי להשלים את מה שזמין ב-CrUX.
אבחון אינטראקציות איטיות במעבדה
מומלץ להתחיל לבדוק במעבדה אחרי שיש לכם נתונים מהשטח שמצביעים על אינטראקציות איטיות. אם אין נתונים מהשטח, יש כמה אסטרטגיות לזיהוי אינטראקציות איטיות במעבדה. האסטרטגיות האלה כוללות מעקב אחרי תהליכי משתמש נפוצים ובדיקת אינטראקציות לאורך הדרך, וגם אינטראקציה עם הדף במהלך הטעינה – כשהשרשור הראשי בדרך כלל הכי עמוס – כדי לזהות אינטראקציות איטיות במהלך החלק הקריטי הזה של חוויית המשתמש.
אופטימיזציה של אינטראקציות
אחרי שמזהים אינטראקציה איטית ואפשר לשחזר אותה באופן ידני במעבדה, השלב הבא הוא לבצע אופטימיזציה שלה.
אפשר לחלק את האינטראקציות לשלושה חלקים:
- השהיה לאחר קלט ראשוני, שמתחילה כשהמשתמש יוזם אינטראקציה עם הדף, ומסתיימת כשהפונקציות להחזרת ערך של האירוע מתחילות לפעול.
- משך העיבוד, שכולל את הזמן שנדרש להפעלת הקריאות החוזרות של האירועים עד להשלמתן.
- השהיית ההצגה, שהיא הזמן שנדרש לדפדפן כדי להציג את הפריים הבא שמכיל את התוצאה הוויזואלית של האינטראקציה.
סכום שלושת חלקי המשנה האלה הוא זמן האחזור הכולל של האינטראקציה. כל חלק משני של אינטראקציה תורם כמות מסוימת של זמן לזמן האחזור הכולל של האינטראקציה, ולכן חשוב לדעת איך אפשר לבצע אופטימיזציה של כל חלק באינטראקציה כדי שהיא תפעל כמה שפחות זמן.
זיהוי והפחתה של השהיה לאחר קלט
כשמשתמש מבצע אינטראקציה עם דף, החלק הראשון של האינטראקציה הזו הוא השהיה לאחר קלט ראשוני. בהתאם לפעילות אחרת בדף, עיכובים בהזנת נתונים יכולים להיות ארוכים למדי. יכול להיות שהסיבה לכך היא פעילות שמתרחשת ב-thread הראשי (אולי בגלל טעינה, ניתוח והידור של סקריפטים), טיפול באחזור, פונקציות של טיימר או אפילו אינטראקציות אחרות שמתרחשות ברצף מהיר וחופפות זו לזו.
לא משנה מה המקור של עיכוב הקלט באינטראקציה, כדאי לצמצם את עיכוב הקלט למינימום כדי שהאינטראקציות יוכלו להתחיל להפעיל את קריאות החזרה (callback) של האירועים בהקדם האפשרי.
הקשר בין הערכת סקריפט לבין משימות ארוכות במהלך ההפעלה
היבט קריטי של אינטראקטיביות במחזור החיים של הדף הוא במהלך ההפעלה. כשדף נטען, הוא עובר עיבוד ראשוני, אבל חשוב לזכור שגם אם דף עבר עיבוד, זה לא אומר שהטעינה שלו הסתיימה. בהתאם למספר המשאבים שדף צריך כדי להיות פונקציונלי לחלוטין, יכול להיות שהמשתמשים ינסו לבצע אינטראקציה עם הדף בזמן שהוא עדיין נטען.
אחד הדברים שיכולים להאריך את השהיית הקלט של אינטראקציה בזמן טעינת דף הוא הערכת סקריפט. אחרי שקובץ JavaScript מאוחזר מהרשת, הדפדפן עדיין צריך לבצע פעולות לפני שקוד ה-JavaScript יכול לפעול. הפעולות האלה כוללות ניתוח של סקריפט כדי לבדוק שהתחביר שלו תקין, הידור שלו לקוד בייט ואז הפעלה שלו.
בהתאם לגודל הסקריפט, העבודה הזו יכולה ליצור משימות ארוכות בשרשור הראשי, שיגרמו לעיכוב בתגובה של הדפדפן לאינטראקציות אחרות של המשתמשים. כדי שהדף יגיב לקלט של המשתמשים במהלך טעינת הדף, חשוב להבין מה אפשר לעשות כדי להפחית את הסיכוי למשימות ארוכות במהלך טעינת הדף, וכך לשמור על מהירות הדף.
אופטימיזציה של קריאות חוזרות (callbacks) של אירועים
השהיית הקלט היא רק החלק הראשון של מה שנמדד על ידי INP. בנוסף, צריך לוודא שפונקציות הקריאה החוזרת (callback) של האירועים שמופעלות בתגובה לאינטראקציה של משתמש יושלמו במהירות האפשרית.
העברה ל-Thread הראשי לעיתים קרובות
ההמלצה הכללית הכי טובה לאופטימיזציה של קריאות חוזרות (callback) לאירועים היא לבצע בהן כמה שפחות פעולות. עם זאת, יכול להיות שהלוגיקה של האינטראקציה מורכבת, ושאפשר רק להפחית באופן שולי את העבודה שהם מבצעים.
אם זה המצב באתר שלכם, הדבר הבא שאתם יכולים לנסות הוא לפצל את העבודה בפונקציות הקריאה החוזרת (callback) של האירועים למשימות נפרדות. כך העבודה המשותפת לא הופכת למשימה ארוכה שחוסמת את ה-thread הראשי, ואינטראקציות אחרות שממתינות ב-thread הראשי יכולות לפעול מוקדם יותר.
setTimeout היא דרך אחת לפצל משימות, כי הקריאה החוזרת שמועברת אליה פועלת במשימה חדשה. אפשר להשתמש בפונקציה setTimeout בפני עצמה או להשתמש בה באופן מופשט בפונקציה נפרדת כדי להשיג תפוקה ארגונומית יותר.
עדיף להעביר את השליטה ל-main thread באופן לא סלקטיבי מאשר לא להעביר אותה בכלל. עם זאת, יש דרך מתוחכמת יותר להעביר את השליטה ל-main thread, והיא כוללת העברה מיידית רק אחרי קריאה חוזרת (callback) של אירוע שמעדכן את ממשק המשתמש, כדי שהלוגיקה של הרינדור תפעל מוקדם יותר.
העברה כדי לאפשר עבודת רינדור מוקדמת יותר
טכניקה מתקדמת יותר של העברת שליטה כוללת את מבנה הקוד בקריאות החוזרות (callbacks) של האירועים, כדי להגביל את מה שמופעל רק ללוגיקה שנדרשת להחלת עדכונים חזותיים עבור הפריים הבא. כל השאר אפשר לדחות למשימה הבאה. השיטה הזו לא רק שומרת על קלות וגמישות של הקוד של פונקציות ה-callback, אלא גם משפרת את זמן העיבוד של האינטראקציות, כי היא לא מאפשרת לעדכונים חזותיים לחסום את הקוד של פונקציות ה-callback של האירועים.
לדוגמה, נניח שיש כלי לעריכת טקסט עשיר שמבצע עיצוב של הטקסט בזמן ההקלדה, אבל גם מעדכן היבטים אחרים בממשק המשתמש בתגובה למה שכתבתם (כמו ספירת מילים, הדגשה של שגיאות איות ומשוב חזותי חשוב אחר). בנוסף, יכול להיות שהאפליקציה תצטרך לשמור את מה שכתבתם, כדי שאם תצאו ותחזרו לא תאבדו את העבודה.
בדוגמה הזו, ארבעת הדברים הבאים צריכים לקרות בתגובה לתווים שהמשתמש הקליד. עם זאת, צריך לבצע רק את הפריט הראשון לפני שהפריים הבא מוצג.
- מעדכנים את תיבת הטקסט עם מה שהמשתמש הקליד ומחילים את העיצוב הנדרש.
- לעדכן את החלק בממשק המשתמש שבו מוצג מספר המילים הנוכחי.
- הפעלת לוגיקה כדי לבדוק אם יש שגיאות איות.
- שמירת השינויים האחרונים (באופן מקומי או במסד נתונים מרוחק).
הקוד לביצוע הפעולה הזו יכול להיראות כך:
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);
});
});
בתרשים הבא אפשר לראות איך דחייה של עדכונים לא קריטיים עד אחרי הפריים הבא יכולה לקצר את משך העיבוד וכך להפחית את זמן האחזור הכולל של האינטראקציה.
השימוש ב-setTimeout() בתוך קריאה ל-requestAnimationFrame() בדוגמת הקוד הקודמת הוא קצת אזוטרי, אבל זו שיטה יעילה שפועלת בכל הדפדפנים כדי למנוע מקוד לא קריטי לחסום את הפריים הבא.
איך להימנע משינויים בפריסה
בעיה בביצועים של רינדור שנקראת 'התנגשות פריסה' (או לפעמים 'פריסה סינכרונית מאולצת') – בעיה שבה הפריסה מתבצעת באופן סינכרוני. הבעיה מתרחשת כשמעדכנים סגנונות ב-JavaScript, ואז קוראים אותם באותה משימה – ויש הרבה מאפיינים ב-JavaScript שיכולים לגרום לבעיה הזו.
הבעיה של שיבוש הפריסה היא צוואר בקבוק בביצועים, כי כשמעדכנים סגנונות ואז מבקשים מיד את הערכים של הסגנונות האלה ב-JavaScript, הדפדפן נאלץ לבצע עבודת פריסה סינכרונית, שאחרת הוא היה יכול לחכות ולבצע אותה באופן אסינכרוני מאוחר יותר, אחרי שפונקציות ה-callback של האירועים יסיימו לפעול.
צמצום העיכוב בהצגת התגובה
השהיית ההצגה של אינטראקציה מתחילה אחרי שפונקציות ה-callback של האירוע מסיימות לפעול, ומסתיימת ברגע שבו הדפדפן יכול לצייר את הפריים הבא שמציג את השינויים הוויזואליים שנוצרו.
מזעור גודל ה-DOM
כשמודל ה-DOM של דף מסוים קטן, בדרך כלל עבודת הרינדור מסתיימת במהירות. עם זאת, כשמסמכי ה-DOM גדולים מאוד, עבודת הרינדור נוטה לגדול עם הגדלת גודל ה-DOM. הקשר בין עבודת הרינדור לבין גודל ה-DOM הוא לא ליניארי, אבל רינדור של DOM גדול דורש יותר עבודה מאשר רינדור של DOM קטן. גודל DOM גדול הוא בעייתי בשני מקרים:
- במהלך העיבוד הראשוני של הדף, כש-DOM גדול דורש הרבה עבודה כדי לעבד את המצב הראשוני של הדף.
- בתגובה לאינטראקציה של משתמש, שבה DOM גדול יכול לגרום לעדכוני רינדור יקרים מאוד, ולכן להגדיל את הזמן שנדרש לדפדפן להציג את הפריים הבא.
חשוב לזכור שיש מקרים שבהם אי אפשר לצמצם באופן משמעותי DOM גדול. יש גישות שבהן אפשר להשתמש כדי להקטין את גודל ה-DOM, כמו השטחת ה-DOM או הוספה ל-DOM במהלך אינטראקציות עם המשתמשים כדי לשמור על גודל ה-DOM הראשוני קטן, אבל הטכניקות האלה יכולות לעזור רק במידה מסוימת.
שימוש ב-content-visibility כדי לבצע רינדור עצלני של רכיבים שלא מוצגים במסך
אחת הדרכים להגביל את כמות עבודת הרינדור במהלך טעינת הדף ואת עבודת הרינדור בתגובה לאינטראקציות של משתמשים היא להסתמך על מאפיין ה-CSS content-visibility, שבעצם מאפשר לבצע רינדור של רכיבים רק כשהם מתקרבים לאזור התצוגה. יכול להיות שיידרש תרגול כדי להשתמש ב-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) מ-Unsplash, מאת David Pisnoy. התמונה שונתה בהתאם לרישיון Unsplash.