חוויית ניפוי באגים משופרת
בחודשים האחרונים, צוות כלי הפיתוח ל-Chrome עבד בשיתוף עם צוות Angular כדי להשיק שיפורים בחוויית ניפוי הבאגים ב-Chrome DevTools. אנשים משני הצוותים עבדו יחד ונקטו פעולות כדי לאפשר למפתחים לנפות באגים ולבצע ניתוח פרופיל של אפליקציות אינטרנט מנקודת המבט של המחבר: מבחינת שפת המקור ומבנה הפרויקט, עם גישה למידע מוכר ורלוונטי.
במאמר הזה נציג את השינויים שנדרשו ב-Angular ובכלי הפיתוח של Chrome כדי להשיג את התוצאה הזו. חלק מהשינויים האלה מוצגים באמצעות Angular, אבל אפשר ליישם אותם גם בפלטפורמות אחרות. צוות Chrome DevTools ממליץ למסגרות אחרות לאמץ את ממשקי ה-API החדשים של המסוף ואת נקודות ההרחבה של מפת המקור, כדי שגם הן יוכלו להציע למשתמשים חוויית ניפוי באגים טובה יותר.
קוד רשימת התעלמות
כשמתבצעת ניפוי באגים באפליקציות באמצעות כלי הפיתוח ל-Chrome, בדרך כלל המחברים רוצים לראות רק את הקוד שלהם, ולא את הקוד של המסגרת שמתחתיו או תלות כלשהי שמסתתרת בתיקייה node_modules
.
כדי לעשות זאת, צוות כלי הפיתוח השיק תוסף למפות מקור שנקרא x_google_ignoreList
. התוסף הזה משמש לזיהוי מקורות של צד שלישי, כמו קוד של מסגרת או קוד שנוצר על ידי חבילה. כשבמסגרת נעשה שימוש בתוסף הזה, המחברים יכולים עכשיו להימנע באופן אוטומטי מקוד שהם לא רוצים לראות או לעבור עליו בלי שתצטרכו להגדיר זאת מראש באופן ידני.
בפועל, כלי הפיתוח ל-Chrome יכולים להסתיר באופן אוטומטי קוד שזוהה ככזה בנתוני מעקב ה-stack, בעץ המקורות, בתיבת הדו-שיח 'פתיחה מהירה', וגם לשפר את ההתנהגות של השלבים וההמשך בנסוך הבאגים.
התוסף של מפת המקור x_google_ignoreList
במפות המקור, השדה החדש x_google_ignoreList
מתייחס למערך sources
ומפרט את האינדקסים של כל המקורות הידועים של צד שלישי במפת המקור הזו. כשמנתחים את מפת המקור, הכלים למפתחים ב-Chrome משתמשים בנתונים האלה כדי לקבוע אילו קטעים בקוד צריכים להיכלל ברשימת הפריטים להתעלמות.
בהמשך מופיעה מפת מקור של קובץ שנוצר, out.js
. יש שני sources
מקוריים שתרמו ליצירת קובץ הפלט: foo.js
ו-lib.js
. הקוד הראשון הוא קוד שכתב מפתח אתר, והקוד השני הוא מסגרת שבה הוא השתמש.
{
"version" : 3,
"file": "out.js",
"sourceRoot": "",
"sources": ["foo.js", "lib.js"],
"sourcesContent": ["...", "..."],
"names": ["src", "maps", "are", "fun"],
"mappings": "A,AAAB;;ABCDE;"
}
ה-sourcesContent
נכלל בשני המקורות המקוריים האלה, וקבצים אלה יוצגו כברירת מחדל בכלי הפיתוח ל-Chrome בכלי לניפוי באגים:
- כקבצים בעץ Sources.
- תיפתח תיבת הדו-שיח 'פתיחה מהירה'.
- כמיקומים ממופה של מסגרת קריאה בנתוני מעקב סטאק של שגיאות בזמן השהיה בנקודת עצירה ובזמן ביצוע שלבים.
עכשיו אפשר לכלול במפות המקור עוד פרט מידע כדי לזהות איזה מהמקורות האלה הוא קוד ראשוני או קוד של צד שלישי:
{
...
"sources": ["foo.js", "lib.js"],
"x_google_ignoreList": [1],
...
}
השדה החדש x_google_ignoreList
מכיל אינדקס יחיד שמתייחס למערך sources
: 1. המשמעות היא שהאזורים שממופים אל lib.js
הם למעשה קוד של צד שלישי שצריך להוסיף באופן אוטומטי לרשימת הפריטים להתעלמות.
בדוגמה מורכבת יותר, שמוצגת בהמשך, המדדים 2, 4 ו-5 מציינים שהאזורים שממופים אל lib1.ts
, lib2.coffee
ו-hmr.js
הם כולם קוד של צד שלישי שצריך להוסיף באופן אוטומטי לרשימת ההתעלמות.
{
...
"sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
"x_google_ignoreList": [2, 4, 5],
...
}
אם אתם מפתחים של מסגרת או של חבילה, עליכם לוודא שמפות המקור שנוצרות במהלך תהליך ה-build כוללות את השדה הזה כדי להתחבר ליכולות החדשות האלה ב-Chrome DevTools.
x_google_ignoreList
ב-Angular
החל מ-Angular v14.1.0, התוכן של התיקיות node_modules
ו-webpack
סומן כ'לצורך התעלמות'.
השגנו זאת באמצעות שינוי ב-angular-cli
על ידי יצירת פלאגין שמתחבר למודול Compiler
של webpack.
הפלאגין של webpack שהמהנדסים שלנו יצרו מתחבר לשלב PROCESS_ASSETS_STAGE_DEV_TOOLING
ומאכלס את השדה x_google_ignoreList
במפות המקור של הנכסים הסופיים ש-webpack יוצר והדפדפן טוען.
const map = JSON.parse(mapContent) as SourceMap;
const ignoreList = [];
for (const [index, path] of map.sources.entries()) {
if (path.includes('/node_modules/') || path.startsWith('webpack/')) {
ignoreList.push(index);
}
}
map[`x_google_ignoreList`] = ignoreList;
compilation.updateAsset(name, new RawSource(JSON.stringify(map)));
מעקבי ערימה מקושרים
נתיבי סטאק עונים על השאלה איך הגעתי לכאן, אבל לרוב הם עושים זאת מנקודת המבט של המכונה, ולא בהכרח תואמים לנקודת המבט של המפתח או למודל המנטלי שלו לגבי זמן הריצה של האפליקציה. זה נכון במיוחד כשפעולות מסוימות מתוזמנות להתרחש באופן אסינכרוני מאוחר יותר: עדיין יכול להיות שיהיה מעניין לדעת מהו "הגורם לבעיה" או הצד של תזמון הפעולות האלה, אבל זה בדיוק משהו שלא יהיה חלק ממעקב אסינכרוני אחר סטאק.
ל-V8 יש מנגנון פנימי למעקב אחרי משימות אסינכרוניות כאלה כשמשתמשים בפרימיטיבים רגילים של תזמון בדפדפן, כמו setTimeout
. הפעולה הזו מתבצעת כברירת מחדל במקרים האלה, כך שהמפתחים יכולים לבדוק אותם כבר עכשיו. אבל בפרויקטים מורכבים יותר, זה לא פשוט כל כך, במיוחד כשמשתמשים במסגרת עם מנגנוני תזמון מתקדמים יותר – למשל, מסגרת שמבצעת מעקב אחר תחומים, שמציבה משימות בהמתנה בהתאמה אישית או שמחלקת עדכונים לכמה יחידות עבודה שפועלות לאורך זמן.
כדי לטפל בבעיה הזו, DevTools חושף מנגנון שנקרא 'Async Stack Tagging API' באובייקט console
, שמאפשר למפתחי המסגרת להציע רמזים לגבי המיקומים שבהם הפעולות מתוזמנות וגם לגבי המיקומים שבהם הפעולות האלה מתבצעות.
Async Stack Tagging API
בלי תיוג סטאק אסינכרוני, נתוני מעקב הסטאק של קוד שמופעל באופן אסינכרוני בדרכים מורכבות על ידי מסגרות מופיעים ללא קשר לקוד שבו הוא תוזמן.
בעזרת Async Stack Tagging אפשר לספק את ההקשר הזה, ודוח הקריסות האסינכרוני נראה כך:
כדי לעשות זאת, משתמשים בשיטה חדשה של console
בשם console.createTask()
ש-Async Stack Tagging API מספק. החתימה שלו היא:
interface Console {
createTask(name: string): Task;
}
interface Task {
run<T>(f: () => T): T;
}
קריאה ל-console.createTask()
מחזירה מופע של Task
, שאפשר להשתמש בו מאוחר יותר כדי להריץ את הקוד האסינכרוני.
// Task Creation
const task = console.createTask(name);
// Task Execution
task.run(f);
אפשר גם להטמיע פעולות אסינכרוניות בתוך פעולות אחרות, ו'הסיבות העיקריות' יוצגו ברצף ב-stack trace.
אפשר להריץ משימות כמה פעמים שרוצים, ועומס העבודה יכול להיות שונה בכל הפעלה. סטאק הקריאות באתר התזמון יישמר עד שמערכת האיסוף תפנה את האובייקט של המשימה.
ה-API של תיוג Async Stack ב-Angular
ב-Angular בוצעו שינויים ב-NgZone – הקשר הביצוע של Angular שנשאר קבוע במשימות אסינכררוניות.
כשמתזמנים משימה, המערכת משתמשת ב-console.createTask()
אם הוא זמין. המכונה Task
שנוצרת מאוחסנת לשימוש נוסף. כשמפעילים את המשימה, NgZone משתמש במכונה השמורה של Task
כדי להריץ אותה.
השינויים האלה נוספו ל-NgZone 0.11.8 של Angular באמצעות בקשות משיכה (pull requests) #46693 ו-#46958.
מסגרות ידידותיות לשיחות
כשמפתחים פרויקט, רוב המסגרות יוצרות קוד מכל סוגי שפות התבניות. למשל, תבניות של Angular או JSX שממירות קוד שנראה כמו HTML ל-JavaScript רגיל שפועל בסופו של דבר בדפדפן. לפעמים, לפונקציות כאלה שנוצרות ניתנים שמות לא ידידותיים במיוחד – שמות של אות אחת אחרי שהן עוברות אופטימיזציה למינימום או שמות מעורפלים או לא מוכרים, גם אם הם לא כאלה.
ב-Angular, לא נדיר לראות מסגרות קריאה עם שמות כמו AppComponent_Template_app_button_handleClick_1_listener
בנתוני מעקב הסטאק.
כדי לטפל בבעיה הזו, כלי הפיתוח של Chrome תומכים עכשיו בשינוי השם של הפונקציות האלה באמצעות מפות מקור. אם במפת המקור יש רשומה של שם לתחילת היקף הפונקציה (כלומר, סוגר הסוגריים הימני של רשימת הפרמטרים), השם הזה אמור להופיע בפריטים של סטאק טריי בפריים הקריאה.
מסגרות קריאה ידידותיות ב-Angular
שינוי השם של מסגרות הקריאה ב-Angular הוא תהליך מתמשך. אנחנו צופים שהשיפורים האלה יתווספו בהדרגה עם הזמן.
במהלך ניתוח התבניות של ה-HTML שהכותבים כתבו, המהדר של Angular יוצר קוד TypeScript, שבסופו של דבר עובר תרגום לקוד JavaScript שהדפדפן טוען ומריץ.
כחלק מתהליך יצירת הקוד, נוצרות גם מפות מקור. אנחנו בודקים כרגע דרכים לכלול שמות של פונקציות בשדה 'names' במפות המקור, ולהפנות לשמות האלה במיפויים בין הקוד שנוצר לקוד המקורי.
לדוגמה, אם נוצרת פונקציה למעקב אחרי אירועים והשם שלה לא ידידותי או הוסר במהלך המינימיזציה, מפות המקור יכולות עכשיו לכלול את השם הידידותי יותר של הפונקציה הזו בשדה 'names', והמיפוי של תחילת היקף הפונקציה יכול עכשיו להפנות לשם הזה (כלומר, סוגר הסוגריים הימני של רשימת הפרמטרים). לאחר מכן, Chrome DevTools ישתמש בשמות האלה כדי לשנות את השמות של מסגרות הקריאה בנתוני מעקב הסטאק.
במבט קדימה
השימוש ב-Angular כתוכנית טייס לבדיקה של העבודה שלנו היה חוויה נהדרת. נשמח לשמוע ממפתחי המסגרות ולקבל משוב על נקודות ההרחבה האלה.
יש עוד תחומים שאנחנו רוצים לבדוק. במיוחד, איך לשפר את חוויית היצירה של פרופילים ב-DevTools.