מהו עיכוב קלט ואיך אפשר לצמצם אותו כדי לשפר את האינטראקטיביות.
אינטראקציות באינטרנט הן דבר מורכב, ומתרחשת בהן פעילות מגוונת בדפדפן. אבל מה שמשותף לכולם הוא שקיימת השהיית קלט מסוימת לפני שפונקציות ה-callback של האירועים מתחילות לפעול. במדריך הזה נסביר מהו עיכוב קלט ומה אפשר לעשות כדי לצמצם אותו, וכך להאיץ את האינטראקציות באתר שלכם.
מהי השהיה לאחר קלט?
השהיה לאחר קלט ראשוני היא משך הזמן שמתחיל ברגע שבו המשתמש יוצר אינטראקציה עם דף – למשל, הקשה על המסך, לחיצה עם העכבר או לחיצה על מקש – עד לרגע שבו מתחילות לפעול פונקציות ה-callback של האירוע שקשור לאינטראקציה. כל אינטראקציה מתחילה עם עיכוב קלט מסוים.
חלק מהעיכוב בקלט הוא בלתי נמנע: תמיד לוקח זמן מסוים למערכת ההפעלה לזהות אירוע קלט ולהעביר אותו לדפדפן. עם זאת, החלק הזה של עיכוב הקלט לרוב לא מורגש, ויש דברים אחרים שקורים בדף עצמו שיכולים לגרום לעיכובים ארוכים מספיק בקלט כדי ליצור בעיות.
איך חושבים על השהיה לאחר קלט
באופן כללי, כדאי שכל חלק באינטראקציה יהיה קצר ככל האפשר, כדי שהאתר שלכם יעמוד בסף ה'טוב' של מדד מהירות התגובה לאינטראקציה באתר (INP), ללא קשר למכשיר של המשתמש. שמירה על עיכוב קלט נמוך היא רק חלק אחד מהדרישות כדי לעמוד בסף הזה.
לכן, כדאי לשאוף לזמן השהיה הקצר ביותר שאפשר כדי לעמוד בסף ה'טוב' של INP. עם זאת, חשוב לדעת שאי אפשר לצפות לביטול מוחלט של עיכובים בהזנה. כל עוד אתם נמנעים מעבודה מוגזמת ב-thread הראשי בזמן שהמשתמשים מנסים ליצור אינטראקציה עם הדף, עיכוב הקלט צריך להיות נמוך מספיק כדי למנוע בעיות.
איך מצמצמים את השהיית הקלט
כמו שציינו קודם, אי אפשר להימנע לחלוטין מעיכוב בקלט, אבל מצד שני, אפשר להימנע מחלק מהעיכובים בקלט. כן. אם אתם נתקלים בעיכובים ארוכים בהזנת נתונים, כדאי לקחת בחשבון את הדברים הבאים.
מומלץ להימנע מטיימרים חוזרים שמפעילים עבודה מוגזמת ב-thread הראשי
יש שתי פונקציות נפוצות של טיימר ב-JavaScript שיכולות לתרום לעיכוב בקלט: setTimeout ו-setInterval. ההבדל בין שתי הפונקציות הוא שבפונקציה setTimeout מתזמנים קריאה חוזרת להפעלה אחרי פרק זמן מוגדר. לעומת זאת, הפונקציה setInterval מתזמנת קריאה חוזרת להפעלה כל n אלפיות השנייה ללא הגבלת זמן, או עד שהטיימר מופסק באמצעות clearInterval.
setTimeout לא בעייתית כשלעצמה – למעשה, היא יכולה לעזור להימנע ממשימות ארוכות. עם זאת, זה תלוי במועד שבו מתרחש פסק הזמן, ובשאלה אם המשתמש מנסה לקיים אינטראקציה עם הדף כשפונקציית הקריאה החוזרת של פסק הזמן פועלת.
בנוסף, אפשר להריץ את setTimeout בלולאה או באופן רקורסיבי, ואז הוא מתנהג יותר כמו setInterval, אבל עדיף לא לתזמן את האיטרציה הבאה עד שהקודמת מסתיימת. למרות שהמשמעות היא שהלולאה תעבור לשרשור הראשי בכל פעם שמתבצעת קריאה ל-setTimeout, חשוב לוודא שהקריאה החוזרת שלה לא תגרום לעומס עבודה מוגזם.
הפונקציה setInterval מפעילה קריאה חוזרת במרווחים, ולכן סביר יותר שהיא תפריע לאינטראקציות. הסיבה לכך היא שבניגוד למופע יחיד של קריאה ל-setTimeout, שהיא קריאה חוזרת חד-פעמית שעשויה להפריע לאינטראקציה של המשתמש, האופי החוזר של setInterval מגדיל את הסיכוי שקריאה כזו תפריע לאינטראקציה, וכך יגדל עיכוב הקלט של האינטראקציה.
setInterval, שמשפיע על השהיית הקלט כפי שמוצג בחלונית הביצועים של כלי הפיתוח ל-Chrome. העיכוב הנוסף בקלט גורם להפעלת הקריאות החוזרות (callback) של האירוע עבור האינטראקציה בשלב מאוחר יותר ממה שהיה יכול להיות.
אם הטיימרים מופיעים בקוד של צד ראשון, יש לכם שליטה בהם. כדאי להעריך אם אתם צריכים אותם, או לנסות לצמצם את העבודה בהם ככל האפשר. אבל טיימרים בסקריפטים של צד שלישי הם סיפור אחר. לרוב אין לכם שליטה על הפעולות של סקריפט של צד שלישי, ולכן כדי לתקן בעיות בביצועים של קוד של צד שלישי, צריך לעבוד עם בעלי עניין כדי לקבוע אם סקריפט מסוים של צד שלישי נחוץ. אם הוא נחוץ, צריך ליצור קשר עם ספק הסקריפט של הצד השלישי כדי לקבוע מה אפשר לעשות כדי לתקן בעיות בביצועים שהוא עלול לגרום באתר שלכם.
הימנעות ממשימות ארוכות
אחת הדרכים לצמצם עיכובים ארוכים בקלט היא להימנע ממשימות ארוכות. אם יש לכם עומס עבודה רב ב-thread הראשי שחוסם אותו במהלך אינטראקציות, זה יגרום לעיכוב בקלט לפני שהמשימות הארוכות יסתיימו.
בנוסף לצמצום כמות העבודה שאתם מבצעים במשימה – ומומלץ תמיד לשאוף לבצע כמה שפחות עבודה בשרשור הראשי – אתם יכולים לשפר את מהירות התגובה לקלט של המשתמשים על ידי פיצול משימות ארוכות.
הימנעות מחפיפה בין אינטראקציות
חלק מאתגר במיוחד באופטימיזציה של INP הוא אם יש לכם אינטראקציות שחופפות. חפיפה בין אינטראקציות מתרחשת כשמבצעים אינטראקציה עם רכיב מסוים, ואז מבצעים אינטראקציה נוספת עם הדף לפני שהאינטראקציה הראשונית הספיקה לעבד את הפריים הבא.
מקורות החפיפה בין האינטראקציות יכולים להיות פשוטים כמו משתמשים שמבצעים הרבה אינטראקציות בפרק זמן קצר. זה יכול לקרות כשמשתמשים מקלידים בשדות של טופס, שבהם יכולות להתרחש הרבה אינטראקציות עם המקלדת בפרק זמן קצר מאוד. אם העבודה על אירוע מרכזי יקרה במיוחד – כמו במקרה הנפוץ של שדות השלמה אוטומטית שבהם מתבצעות בקשות רשת לשרת קצה עורפי – יש לכם כמה אפשרויות:
- כדאי לשקול ביטול כפילויות של קלט כדי להגביל את מספר הפעמים שפונקציית הקריאה החוזרת של אירוע מופעלת בפרק זמן נתון.
- כדאי להשתמש ב-
AbortControllerכדי לבטל בקשות יוצאות שלfetch, כדי שה-thread הראשי לא יהיה עמוס מדי בטיפול בקריאות חוזרות שלfetch. הערה: אפשר להשתמש גם במאפייןsignalשל מופעAbortControllerכדי לבטל אירועים.
מקור נוסף לעיכוב בקלט בגלל אינטראקציות חופפות יכול להיות אנימציות עתירות משאבים. בפרט, אנימציות ב-JavaScript עשויות להפעיל הרבה קריאות של requestAnimationFrame, מה שיכול להפריע לאינטראקציות של המשתמשים. כדי לעקוף את הבעיה הזו, מומלץ להשתמש באנימציות CSS ככל האפשר כדי להימנע מהוספה לתור של פריימים של אנימציה שעשויים להיות יקרים. אבל אם עושים את זה, חשוב להימנע מאנימציות לא מורכבות כדי שהאנימציות יפעלו בעיקר ב-GPU ובשרשורי ה-compositor, ולא בשרשור הראשי.
סיכום
יכול להיות שעיכובים בקלט לא מייצגים את רוב הזמן שנדרש להפעלת האינטראקציות, אבל חשוב להבין שכל חלק באינטראקציה תופס כמות זמן שאפשר לצמצם. אם זמן ההשהיה של הקלט ארוך, יש לכם הזדמנויות לקצר אותו. כדי להפחית את זמן התגובה לקלט, וכך לשפר את האינטראקטיביות של האתר עבור המשתמשים, כדאי להימנע מהפעלת פונקציות חוזרות של טיימר, לחלק משימות ארוכות ולשים לב לחפיפה פוטנציאלית בין אינטראקציות.
תמונה ראשית (Hero) מ-Unsplash, מאת Erik Mclean.