קוד JavaScript שפועל בצורה כבדה עלול להשפיע לרעה על ביצועי טעינת הדף ועל האינטראקטיביות. אפליקציות אינטרנט מודרניות מסתמכות על JavaScript יותר מאי פעם, ממקורות רבים. הם כוללים את קוד האפליקציה עצמו (וקוד המסגרת שעליו הוא מסתמך), וגם סקריפטים של צד שלישי שמוסיפים פונקציונליות כמו ווידג'טים של צ'אט ונגני וידאו. גם סקריפטים של ניתוח נתונים ושיווק שפועלים מאחורי הקלעים הם גורמים נפוצים שקל מאוד לשכוח.
זיהוי שורש הבעיה של דף אינטרנט שלא מגיב יכול להיות מסובך, ונדרשת מומחיות מסוימת כדי להפעיל מעקב או פרופיילינג של ביצועי האינטרנט ולהבין את התוצאות. בנוסף, נתוני שדה שונים מאוד מנתוני מעבדה, שמשכפלים רק קבוצת משנה קטנה של תרחישים של משתמשים אמיתיים. במצב כזה, קשה לזהות את שורש הבעיה של ביצועים נמוכים, במיוחד באינטראקציות.
Long Animation Frames API הוא API קל משקל שאפשר להשתמש בו כדי לזהות JavaScript שפועל בצורה כבדה בשטח. סקריפט שפועל בצורה כבדה יכול להיות סקריפט בודד שפועל במשך זמן רב, או סקריפט שפועל כמה פעמים במהלך מחזור החיים של הדף.
איך מטמיעים
המעקב אחרי פריימים אנימציה עם טעינה ארוכה (LoAF) מתבצע באמצעות הממשק PerformanceObserver. הוא פולט רשומה של long-animation-frame כשעיבוד התמונה של פריים של אנימציה נמשך יותר מ-50 אלפיות השנייה. הרשומה מכילה מידע על פריים האנימציה עם הטעינה הארוכה, כולל משך הזמן של הפריים והסקריפטים שהופעלו במהלך הפריים.
הרשומה long-animation-frame מכילה מאפיין scripts שהוא מערך של אובייקטים מסוג PerformanceScript. כל אובייקט PerformanceScript מכיל מידע על הסקריפט שהופעל במהלך פריים האנימציה עם הטעינה הארוכה, כולל ה-sourceURL וה-duration של הסקריפט.
דוגמה לזיהוי הסקריפטים שפועלים הכי הרבה זמן ותורמים לפריימים ארוכים של אנימציה
// Accumulate all script entries across the page lifecycle so no
// data is lost between observer callbacks.
const allScripts = [];
const observer = new PerformanceObserver(list => {
// Collect all script entries across frames to find the biggest offenders.
allScripts.push(...list.getEntries().flatMap(entry => entry.scripts));
// Group by sourceURL so you can identify which scripts contribute
// the most total time, even if each individual invocation is short.
const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
const scriptsBySource = scriptSource.map(sourceURL => ([sourceURL,
allScripts.filter(script => script.sourceURL === sourceURL)
]));
const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
sourceURL,
count: scripts.length,
totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
}));
// Only include scripts above a certain threshold to reduce noise.
const heavyScripts = processedScripts.filter(script => {
return script.totalDuration > 100;
});
// Sort by total duration so the worst offenders appear first,
// making it easier to prioritize optimization efforts.
heavyScripts.sort((a, b) => b.totalDuration - a.totalDuration);
// Log to the console for local debugging. In production, replace
// this with a call to send the data to your analytics service.
console.table(heavyScripts);
});
// Use buffered: true to capture any long frames that occurred before
// this observer was registered.
observer.observe({type: 'long-animation-frame', buffered: true});
שיטות מומלצות
- מומלץ להעדיף את Long Animation Frames API על פני חלופות כמו JS Self-Profiling API, שכולל תקורה גבוהה יותר של זמן ריצה.
- כדאי לסכם את המידע העיקרי, כי Long Animation Frames API מכיל הרבה פרטים.
- כן שולחים את המידע הנדרש לשירות ניתוח נתונים בסביבת הייצור.
תמיכה בדפדפנים ושיטות חלופיות
דפדפנים שלא תומכים ב-Long Animation Frames API מתעלמים ממנו, כך שאפשר להשתמש בו בבטחה בלי חלופות. ברוב המקרים, ההזדמנויות לשיפור הביצועים שהוא מזהה יהיו רלוונטיות גם לדפדפנים אחרים.