חוויה משופרת של ניפוי באגים
בחודשים האחרונים, הצוות של כלי הפיתוח ל-Chrome שיתף פעולה עם צוות Angular כדי להשיק שיפורים לחוויית ניפוי הבאגים ב-Chrome DevTools. אנשים משני הצוותים עבדו יחד כדי לאפשר למפתחים לנפות באגים וליצור פרופילים באפליקציות אינטרנט מנקודת המבט של המחבר: מבחינת שפת המקור ומבנה הפרויקט, עם גישה למידע מוכר ורלוונטי להם.
בפוסט הזה מפורט אילו שינויים ב-Angular ובכלי הפיתוח ל-Chrome נדרשים כדי להשיג את המטרה הזו. אף על פי שחלק מהשינויים האלה מוצגים באמצעות Angular, אפשר להחיל אותם גם על מסגרות אחרות. צוות כלי הפיתוח ל-Chrome מעודד מסגרות אחרות לאמץ את ממשקי ה-API החדשים של המסוף ואת נקודות התוספים של מפת המקור, כדי שגם הם יוכלו להציע למשתמשים שלהם חוויה טובה יותר של ניפוי באגים.
התעלמות מקוד מרישום
במהלך ניפוי באגים באפליקציות באמצעות כלי הפיתוח ל-Chrome, מחברים בדרך כלל רוצים לראות רק את הקוד שלהם, ולא את הקוד של ה-framework שמתחתיו או את תלות מסוימת שמוסתרת בתיקייה node_modules
.
כדי לעשות זאת, צוות כלי הפיתוח הוסיף תוסף למפות מקור בשם x_google_ignoreList
. התוסף הזה משמש לזיהוי מקורות של צד שלישי, כמו קוד framework או קוד שנוצר על ידי Bundler. כאשר framework משתמש בתוסף הזה, מחברים נמנעים עכשיו באופן אוטומטי מקוד שהם לא רוצים לראות או לעבור עליו בלי להגדיר אותו מראש באופן ידני.
בפועל, כלי הפיתוח ל-Chrome יכולים להסתיר באופן אוטומטי קוד שזוהה כך בדוחות הקריסות, בעץ המקורות ובתיבת הדו-שיח 'פתיחה מהירה', וגם לשפר את התנהגות השלבים וההמשך בכלי לניפוי באגים.
התוסף של מפת המקור 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 יציגו את הקבצים האלה כברירת מחדל בכלי לניפוי באגים:
- כקבצים בעץ המקורות.
- כתוצאות בתיבת הדו-שיח 'פתיחה מהירה'.
- כמיקומים ממופים של מסגרות קריאה בדוחות קריסות של שגיאות בזמן השהיה בנקודת עצירה (breakpoint) ובזמן צעד.
יש עוד פרט מידע אחד שאפשר לכלול עכשיו במפות המקור כדי לזהות איזה מהמקורות הוא קוד ראשון או קוד של צד שלישי:
{
...
"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],
...
}
למפתחים של framework או של Bundler, חשוב לוודא שמפות המקור שנוצרות במהלך תהליך ה-build כוללות את השדה הזה כדי ליהנות מהיכולות החדשות האלה בכלי הפיתוח ל-Chrome.
x_google_ignoreList
ב-Angular
החל מגרסה 14.1.0 של Anngular, תוכן התיקיות node_modules
ו-webpack
סומן כ-"to ignore".
השינוי הזה הושג באמצעות שינוי ב-angular-cli
על ידי יצירת פלאגין שמתחבר למודול Compiler
של Webpack
הפלאגין של Webpack שהמהנדסים שלנו יצרו קטעי hook לשלב PROCESS_ASSETS_STAGE_DEV_TOOLING
ומאכלס את השדה x_google_ignoreList
במפות המקור עבור הנכסים הסופיים שנוצרים והדפדפן נטען.
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
. הבדיקה מתבצעת כברירת מחדל במקרים כאלה, כך שהמפתחים כבר יכולים לבדוק אותם. אבל בפרויקטים מורכבים יותר, זה לא כל כך פשוט, במיוחד כשמשתמשים במסגרת עם מנגנוני תזמון מתקדמים יותר – לדוגמה, מסגרת שמבצעת מעקב אחרי אזורים, סידור משימות בהתאמה אישית או מפצל עדכונים למספר יחידות עבודה שפועלות לאורך זמן.
כדי לפתור את הבעיה, כלי הפיתוח חושפים על האובייקט console
מנגנון שנקרא Async Stack tagging API (ממשק API לתיוג אסינכרוניים ב-Stack API), שמאפשר למפתחים של framework לרמוז גם על המיקומים שבהם הפעולות מתוזמנות וגם על המקומות שבהם הפעולות האלה מתבצעות.
API לתיוג מקבץ אסינכרוני
ללא תיוג של מקבץ אסינכרוניים, דוחות קריסות של קוד שמבוצע באופן אסינכרוני בדרכים מורכבות על ידי frameworks מוצגים ללא חיבור לקוד שבו הוא תוזמן.
באמצעות תיוג ערימה אסינכרוני אפשר לספק את ההקשר הזה, ודוח הקריסות ייראה כך:
כדי לעשות זאת, צריך להשתמש בשיטה console
חדשה בשם console.createTask()
, שמספקת את Async Stack Stack 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);
גם הפעולות האסינכרוניות יכולות להיות מקוננים, ו'הסיבות הבסיסיות' מוצגות בדוח הקריסות ברצף.
אפשר להריץ משימות כמה פעמים שרוצים, והמטען הייעודי (payload) של העבודה יכול להשתנות בין הרצה. ערימת הקריאות באתר התזמון תיזכר עד שאובייקט המשימה ייאסף אשפה.
ה-API לתיוג מקבץ אסינכרוני בזווית
ב-Angular, בוצעו שינויים ב-NgZone – הקשר הביצוע של Angular שנשאר בכל המשימות האסינכרוניות.
כשמתזמנים משימה, נעשה שימוש ב-console.createTask()
(כשהאפשרות זמינה). מופע ה-Task
שנוצר נשמר לשימוש נוסף. אחרי הפעלת המשימה, NgZone ישתמש במופע המאוחסן Task
כדי להריץ אותה.
השינויים האלה הגיעו ל-NgZone 0.11.8 של Angular באמצעות בקשות משיכה #46693 ו-#46958.
מסגרות שיחה ידידותיות
מסגרות בדרך כלל יוצרות קוד מכל סוגי השפות ליצירת תבניות במהלך בניית פרויקט, כמו תבניות Angular או JSX, שהופכות קוד שנראה כ-HTML ל-JavaScript פשוט, שבסופו של דבר ירוץ בדפדפן. לפעמים הסוגים האלה של פונקציות שנוצרו מקבלים שמות לא מאוד ידידותיים – שמות עם אות אחת לאחר ההקטנה או שמות מעורפלים או לא מוכרים, גם אם הם לא זהים.
ב-Angular, פעמים רבות מופיעות מסגרות של שיחות עם שמות כמו AppComponent_Template_app_button_handleClick_1_listener
בדוחות הקריסות.
כדי לפתור את הבעיה, כלי הפיתוח ל-Chrome תומכים עכשיו בשינוי השמות של הפונקציות האלה באמצעות מפות מקור. אם מפת מקור כוללת רשומת שם להתחלה של היקף הרשאות (כלומר, הסוגריים השמאליים של רשימת הפרמטרים), מסגרת הקריאה צריכה להציג את השם הזה בדוח הקריסות.
מסגרות שיחה ידידותיות ב-Angular
שינוי השם של מסגרות קריאה ב-Angular הוא משימה מתמשכת. אנחנו צופים שהשיפורים האלה יוטמעו בהדרגה עם הזמן.
במהלך ניתוח תבניות ה-HTML שנכתבו, המהדר של Angular יוצר קוד TypeScript, שבסופו של דבר עובר לקוד JavaScript שהדפדפן טוען ומפעיל.
כחלק מתהליך יצירת הקוד, נוצרות גם מפות מקור. אנחנו בוחנים כרגע דרכים שונות לכלול שמות של פונקציות בשדה 'שמות' של מפות המקור, ולהתייחס לשמות האלה במיפויים בין הקוד שנוצר לבין הקוד המקורי.
לדוגמה, אם נוצרת פונקציה ל-event listener והשם שלה לא ידידותי או מוסר במהלך ההקטנה, מפות מקור יכולות לכלול עכשיו את השם הידידותי יותר לפונקציה בשדה 'שמות', והמיפוי של תחילת ההיקף של הפונקציה יכול עכשיו להתייחס לשם הזה (כלומר, הסוגריים השמאליים של רשימת הפרמטרים). לאחר מכן כלי הפיתוח ל-Chrome ישתמשו בשמות האלה כדי לשנות את השמות של מסגרות לשיחות בדוחות הקריסות.
במבט קדימה
השימוש ב-Angular כפיילוט לבדיקה לאימות העבודה שלנו, הייתה חוויה נהדרת. נשמח לקבל מידע ממפתחי ה-framework ולשלוח לנו משוב על התוספות האלה.
יש עוד תחומים שאנחנו מעוניינים לחקור. במיוחד איך לשפר את החוויה של יצירת פרופילים בכלי הפיתוח.