ulepszone debugowanie;
W ciągu ostatnich kilku miesięcy zespół Narzędzi deweloperskich w Chrome współpracował z zespołem Angulara nad ulepszeniami debugowania w Narzędziach deweloperskich w Chrome. Pracownicy obu zespołów współpracowali ze sobą i podejmowali działania, aby umożliwić deweloperom debugowanie i profilowanie aplikacji internetowych z perspektywy autora: w języku źródłowym i strukturze projektu, z dostępem do informacji, które są im znane i przydatne.
W tym poście szczegółowo opisujemy, jakie zmiany w Angular i Narzędziach deweloperskich w Chrome były niezbędne, aby osiągnąć ten efekt. Chociaż niektóre z tych zmian pokazujemy w Angular, można je również zastosować na innych platformach. Zespół Chrome DevTools zachęca inne frameworki do korzystania z nowych interfejsów API konsoli i punktów rozszerzenia mapy źródłowej, aby również one mogły oferować użytkownikom lepsze możliwości debugowania.
Kod ignorowania listy
Podczas debugowania aplikacji za pomocą Narzędzi deweloperskich w Chrome autorzy zwykle chcą widzieć tylko swój kod, a nie frameworku lub zależności ukrytej w folderze node_modules
.
W tym celu zespół Narzędzi deweloperskich wprowadził rozszerzenie map źródłowych o nazwie x_google_ignoreList
. To rozszerzenie służy do identyfikowania źródeł zewnętrznych, takich jak kod frameworku lub kod wygenerowany przez pakiet. Gdy platforma korzysta z tego rozszerzenia, autorzy automatycznie unikają kodu, który nie chce zobaczyć ani przejść przez niego bez konieczności ręcznego konfigurowania tego rozszerzenia.
W praktyce Narzędzia deweloperskie w Chrome mogą automatycznie ukrywać kod zidentyfikowany na przykład w zrzutach stosu, w drzewie Źródła i oknie Szybkie otwieranie, a także usprawniają działanie kroków i wznawiania debugera.
Rozszerzenie mapy źródłowej x_google_ignoreList
W mapach źródeł nowe pole x_google_ignoreList
odwołuje się do tablicy sources
i wypisuje indeksy wszystkich znanych źródeł zewnętrznych w tej mapie źródeł. Podczas analizowania mapy źródłowej Narzędzia deweloperskie w Chrome użyją tego parametru, aby ustalić, które sekcje kodu należy pominąć.
Poniżej znajdziesz mapę źródeł dla wygenerowanego pliku out.js
. Istnieją 2 pierwotne elementy sources
, które miały udział w wygenerowaniu pliku wyjściowego: foo.js
i lib.js
. Pierwsza to część napisana przez dewelopera witryny, a druga to framework, którego użył.
{
"version" : 3,
"file": "out.js",
"sourceRoot": "",
"sources": ["foo.js", "lib.js"],
"sourcesContent": ["...", "..."],
"names": ["src", "maps", "are", "fun"],
"mappings": "A,AAAB;;ABCDE;"
}
Parametr sourcesContent
jest uwzględniony w obu tych źródłach, a Narzędzia deweloperskie w Chrome domyślnie wyświetlają te pliki w Debugerze:
- jako pliki w drzewie źródeł.
- W oknie Szybkie otwieranie.
- Jako zmapowane lokalizacje ramek wywołania w zrzutach stosu błędów podczas wstrzymania na punkcie przerwania i podczas przechodzenia.
W mapach źródeł można teraz uwzględnić 1 dodatkowy element informacji, który pozwala określić, które z nich są kodem własnym, a które kodem zewnętrznym:
{
...
"sources": ["foo.js", "lib.js"],
"x_google_ignoreList": [1],
...
}
Nowe pole x_google_ignoreList
zawiera pojedynczy indeks odwołujący się do tablicy sources
: 1. Oznacza to, że regiony zmapowane na lib.js
są w rzeczywistości kodem innej firmy, który powinien zostać automatycznie dodany do listy ignorowanych.
W bardziej skomplikowanym przykładzie pokazanym poniżej indeksy 2, 4 i 5 wskazują, że regiony mapowane na lib1.ts
, lib2.coffee
i hmr.js
to kody innych firm, które powinny zostać automatycznie dodane do listy ignorowanych.
{
...
"sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
"x_google_ignoreList": [2, 4, 5],
...
}
Jeśli jesteś twórcą frameworka lub narzędzia do tworzenia pakietów, upewnij się, że mapy źródeł wygenerowane podczas procesu kompilacji zawierają to pole, aby można było korzystać z tych nowych funkcji w narzędziu Chrome DevTools.
x_google_ignoreList
w Angular
Od wersji Angular 14.1.0 zawartość folderów node_modules
i webpack
została oznaczona jako „do zignorowania”.
Jest to możliwe dzięki zmianie w angular-cli
polegającej na utworzeniu wtyczki, która łączy się z modułem Compiler
pakietu internetowego.
Wtyczka webpack, którą nasi inżynierowie stworzyli na etapie PROCESS_ASSETS_STAGE_DEV_TOOLING
, wypełnia pole x_google_ignoreList
w mapach źródeł dla końcowych zasobów wygenerowanych przez webpack i przesłanych do przeglądarki.
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)));
Połączone zrzuty stosu
Ścieżki stosu odpowiadają na pytanie „jak się tu znalazłem”, ale często jest to widok z perspektywy maszyny i niekoniecznie odpowiada on perspektywie dewelopera lub jego mentalnemu modelowi czasu wykonywania aplikacji. Jest to szczególnie istotne, gdy niektóre operacje zostaną zaplanowane asynchronicznie później: nadal interesujące może być poznanie „głównej przyczyny” lub planowania takich operacji, ale nie jest to częścią asynchronicznego zrzutu stosu.
V8 ma wewnętrzny mechanizm śledzenia takich zadań asynchronicznych, gdy używane są standardowe prymitywy harmonogramowania w przeglądarce, takie jak setTimeout
. W takich przypadkach jest to domyślne, więc deweloperzy mogą już je sprawdzić. W przypadku bardziej złożonych projektów nie jest to jednak takie proste, zwłaszcza gdy używasz platformy z bardziej zaawansowanymi mechanizmami harmonogramowania, np. takiej, która wykonuje śledzenie stref, kolejkowanie niestandardowych zadań lub dzieli aktualizacje na kilka jednostek pracy, które są wykonywane w czasie.
Aby rozwiązać ten problem, DevTools udostępnia mechanizm o nazwie „Interfejs API do oznaczania zadań asynchronicznych” w obiekcie console
, który umożliwia deweloperom frameworków wskazywanie lokalizacji, w których zaplanowano operacje, oraz lokalizacji, w których te operacje są wykonywane.
Async Stack Tagging API
Bez oznaczenia kodu asynchronicznego zrzuty kodu w przypadku kodu, który jest wykonywany asynchronicznie w skomplikowany sposób przez frameworki, nie są powiązane z kodem, w którym zostały zaplanowane.
Dzięki asynchronicznemu tagowaniu stosu można podać ten kontekst, a zrzut stosu będzie wyglądał tak:
Aby to zrobić, użyj nowej metody console
o nazwie console.createTask()
, którą udostępnia interfejs Async Stack Tagging API. Jego podpis wygląda tak:
interface Console {
createTask(name: string): Task;
}
interface Task {
run<T>(f: () => T): T;
}
Wywołanie console.createTask()
zwraca instancję Task
, której można później użyć do uruchomienia kodu asynchronicznego.
// Task Creation
const task = console.createTask(name);
// Task Execution
task.run(f);
Operacje asynchroniczne mogą być również zagnieżdżone, a „przyczyny źródłowe” będą wyświetlane w kolejności w wyświetleniu ścieżki wywołań.
Zadania można uruchamiać dowolną liczbę razy, a ładunek roboczy może się różnić w zależności od uruchomienia. Stos wywołań w miejscu planowania będzie zapamięty, dopóki obiekt zadania nie zostanie usunięty.
Interfejs API do tagowania elementów w aplikacji asynchronicznej w Angular
W Angularze wprowadziliśmy zmiany w NgZone – kontekście wykonania w Angular, który jest trwały w przypadku zadań asynchronicznych.
Podczas planowania zadania używa console.createTask()
, jeśli jest dostępny. Powstała w ten sposób instancja Task
jest przechowywana do dalszego użycia. Po wywołaniu zadania NgZone użyje do jego uruchomienia zapisanej instancji Task
.
Te zmiany zostały wprowadzone w wersji 0.11.8 interfejsu NgZone w ramach pull request #46693 i #46958.
Ramki przyjaznych rozmów
Podczas tworzenia projektu platformy często generują kod z różnych języków szablonów – na przykład szablonów Angular lub JSX, które zmieniają wygląd kodu HTML w zwykły kod JavaScript, który z czasem uruchamia się w przeglądarce. Czasami tego typu wygenerowane funkcje mają nazwy, które nie są zbyt przyjazne – albo nazwy jednoliterowe po ich zminimalizowaniu, albo niejasne lub nieznane nazwy, nawet jeśli nie są one zminimalizowane.
W przypadku Angulara w śladach stosu często występują ramki wywołania o nazwach takich jak AppComponent_Template_app_button_handleClick_1_listener
.
Aby rozwiązać ten problem, narzędzia deweloperskie w Chrome obsługują teraz zmianę nazwy tych funkcji za pomocą map źródłowych. Jeśli mapa źródłowa zawiera wpis z nazwą początku zakresu działania funkcji (czyli lewą nawias w liście parametrów), ramka wywołania powinna wyświetlać tę nazwę w wyświetleniu stosu.
Ramki wywołania przyjazne dla użytkownika w Angular
Zmiana nazw ramek wywołań w Angular jest ciągłym zadaniem. Spodziewamy się, że te ulepszenia będą wprowadzane stopniowo.
Podczas analizowania szablonów HTML napisanych przez autorów kompilator Angular generuje kod TypeScript, który jest następnie przekształcany w kod JavaScript, który przeglądarka wczytuje i uruchamia.
W ramach tego procesu generowania kodu tworzone są też mapy źródeł. Obecnie szukamy sposobów na uwzględnianie nazw funkcji w polu „names” map źródłowych oraz na odwoływanie się do tych nazw w mapowaniu między wygenerowanym kodem a oryginalnym kodem.
Jeśli na przykład wygenerowana zostanie funkcja dla odbiornika zdarzeń, a jej nazwa zostanie usunięta lub zmieniona na nieprzyjazną podczas minimalizacji, mapy źródłowe mogą teraz zawierać w polu „names” bardziej przyjazną nazwę tej funkcji, a mapowanie na początku zakresu funkcji może teraz odwoływać się do tej nazwy (czyli lewej nawiasu listy parametrów). Narzędzia deweloperskie w Chrome będą używać tych nazw do zmiany nazw ramek wywołania w śladach stosu.
Perspektywy
Korzystanie z Angular w ramach programu pilotażowego, aby sprawdzić naszą pracę, było wspaniałym doświadczeniem. Chętnie poznamy opinie deweloperów platformy i przekażemy opinię na temat tych punktów rozszerzeń.
Jest więcej obszarów, które chcemy zbadać. W szczególności chodzi o to, jak ulepszyć profilowanie w Narzędziach deweloperskich.