Nowoczesne debugowanie stron internetowych w Narzędziach deweloperskich w Chrome

Wstęp

Obecnie autorzy mogą tworzyć aplikacje internetowe przy użyciu wielu abstrakcji. Zamiast bezpośredniej interakcji z interfejsami API niższego poziomu dostępnych w ramach platformy internetowej wielu autorów korzysta z platform, narzędzi i kompilatorów, aby pisać swoje aplikacje z perspektywy wyższego poziomu.

Na przykład komponenty utworzone na platformie Angular są tworzone w języku TypeScript i zawierającym szablony HTML. Pod maską interfejs wiersza poleceń i pakiet internetowy Angular kompilują wszystko do języka JavaScript i tworzą tzw. pakiet, który jest następnie wysyłany do przeglądarki.

Podczas debugowania lub profilowania aplikacji internetowych za pomocą Narzędzi deweloperskich możesz obecnie zobaczyć i debugować skompilowaną wersję swojego kodu zamiast faktycznie napisanego. Nie jest to jednak potrzebne zachowanie jako autora:

  • Chcesz debugować skompresowany kod JavaScript, tylko oryginalny.
  • Korzystając z TypeScriptu, nie chcesz debugować JavaScriptu, tylko chcesz debugować oryginalny kod TypeScript.
  • Jeśli używasz szablonów, jak w Angular, Lit lub JSX, nie zawsze chcesz debugować wynikowy DOM. Warto samodzielnie debugować komponenty.

Ogólnie rzecz biorąc, warto debugować własny kod w takiej postaci, w jakiej został napisany.

Mapy źródeł już w pewnym stopniu wypełniają tę lukę, ale Narzędzia deweloperskie w Chrome i ekosystem mogą osiągnąć w tym obszarze o wiele więcej.

Przyjrzyjmy się im bliżej.

Kod stworzony a kod wdrożony

Obecnie podczas poruszania się po drzewie plików w panelu Źródła widoczna jest zawartość skompilowanego – i często zminimalizowanego – pakietu. Rzeczywiste pliki, które są pobierane i uruchamiane przez przeglądarkę. W Narzędziach deweloperskich nosi on nazwę Deployed Code (Wdrożony kod).

Zrzut ekranu przedstawiający drzewo plików w Narzędziach deweloperskich w Chrome, z widocznym wdrożonym kodem.

Nie jest to proste i często trudne do zrozumienia. Jako autor chcesz wyświetlić i debugować napisany przez siebie kod, a nie wdrożony kod.

Aby to zrekompensować, możesz teraz wyświetlać w drzewie kod autora. Dzięki temu drzewo bardziej przypomina pliki źródłowe, które można zobaczyć w IDE, a te pliki są teraz rozdzielone od Wdrożonego kodu.

Zrzut ekranu przedstawiający drzewo plików w Narzędziach deweloperskich w Chrome, z widocznym kodem autorstwa.

Aby włączyć tę opcję w Narzędziach deweloperskich w Chrome, otwórz Ustawienia > Eksperymenty i zaznacz Grupuj źródła w drzewa utworzone i wdrożone.

Zrzut ekranu przedstawiający ustawienia Narzędzi deweloperskich.

„Tylko mój kod”

Gdy korzystasz z zależności lub pracujesz nad strukturą, pliki innych firm mogą Ci w tym przeszkadzać. Zwykle chcesz zobaczyć tylko swój kod, a nie kod z biblioteki zewnętrznej ukrytej w folderze node_modules.

Aby to zrekompensować, w Narzędziach deweloperskich domyślnie włączone jest dodatkowe ustawienie: Automatycznie dodawaj znane skrypty innych firm do listy ignorowanych. Znajdziesz go, klikając DevTools > DevTools > DevTools.

Zrzut ekranu przedstawiający ustawienia Narzędzi deweloperskich.

Gdy to ustawienie jest włączone, Narzędzia deweloperskie ukrywają każdy plik lub folder, który platforma lub narzędzie do kompilacji oznaczyło jako do ignorowania.

Od Angular w wersji 14.1.0 zawartość folderów node_modules i webpack jest tak oznaczona. Dlatego te foldery, zawarte w nich pliki i inne artefakty zewnętrzne nie są widoczne w różnych miejscach w Narzędziach deweloperskich.

Jako autor nie musisz nic robić, aby włączyć to nowe zachowanie. Wprowadzenie tej zmiany należy do zasad.

Kod z listy ignorowanych w zrzucie stosu

Pliki z listy ignorowanych nie pojawiają się już w zrzucie stosu. Jako autor masz teraz dostęp do bardziej trafnych zrzutów stosu.

Zrzut ekranu przedstawiający zrzut stosu w Narzędziach deweloperskich.

Jeśli chcesz zobaczyć wszystkie ramki wywołań stosu zrzutu, kliknij link Pokaż więcej ramek.

To samo dotyczy stosów wywołań, które widzisz podczas debugowania i przeglądania kodu. Gdy platformy lub usługi tworzenia pakietów przekazują w Narzędziach deweloperskich informacje o skryptach innych firm, podczas debugowania kroku narzędzia te automatycznie ukrywają wszystkie nieistotne ramki wywołań i przeskakują nad wszelkimi kodami z listy ignorowanych.

Zrzut ekranu przedstawiający debugera źródeł Narzędzi deweloperskich podczas debugowania.

Ignorowany kod w drzewie plików

Aby ukryć pliki i foldery na liście ignorowanych w drzewie plików Kod autora w panelu Źródła, w Narzędziach deweloperskich zaznacz Ukryj kod z listy ignorowanych w widoku drzewa źródeł w sekcji Ustawienia > Eksperymenty.

Zrzut ekranu przedstawiający ustawienia Narzędzi deweloperskich.

W przykładowym projekcie Angular foldery node_modules i webpack są teraz ukryte.

Zrzut ekranu przedstawiający drzewo plików w Narzędziach deweloperskich w Chrome, z widocznym kodem utworzonym, ale bez node_modules.

Ignorowany kod w menu „Szybkie otwieranie”

Ignorowany kod jest nie tylko ukryty w drzewie plików, ale także ukryty w menu „Szybkie otwieranie” (Control+P (Linux/Windows) lub Command+P (Mac)).

Zrzut ekranu przedstawiający Narzędzia deweloperskie z menu „Szybkie otwieranie”.

Więcej ulepszeń zrzutów stosu

Po omówieniu odpowiednich zrzutów stosu w Narzędziach deweloperskich w Chrome wprowadziliśmy jeszcze więcej ulepszeń.

Połączone zrzuty stosu

Jeśli niektóre operacje mają być wykonywane asynchronicznie, zrzuty stosu w Narzędziach deweloperskich przekazują obecnie tylko część historii.

Oto na przykład bardzo prosty algorytm szeregowania w hipotetycznym pliku framework.js:

function makeScheduler() {
  const tasks = [];

  return {
    schedule(f) {
      tasks.push({ f });
    },

    work() {
      while (tasks.length) {
        const { f } = tasks.shift();
        f();
      }
    },
  };
}

const scheduler = makeScheduler();

function loop() {
  scheduler.work();
  requestAnimationFrame(loop);
};

loop();

...oraz jak deweloper może go użyć we własnym kodzie w pliku example.js:

function someTask() {
  console.trace("done!");
}

function businessLogic() {
  scheduler.schedule(someTask);
}

businessLogic();

Podczas dodawania punktu przerwania w metodzie someTask lub podczas badania logu czasu wyświetlanego w konsoli nie widzisz żadnego wywołania businessLogic(), które było „główną przyczyną” tej operacji.

Zamiast tego widzisz tylko logikę harmonogramu platformy, która doprowadziła do wykonania zadania, a w zrzucie stosu nie ma menu nawigacyjnego, które pomagałoby w ustaleniu powiązań między zdarzeniami prowadzącymi do tego zadania.

Zrzut stosu z wykonanym asynchronicznym kodem bez informacji o czasie jego zaplanowania.

Dzięki nowej funkcji o nazwie „Asynchroniczne tagowanie stosu” można opowiedzieć całą historię, łącząc ze sobą obie części kodu asynchronicznego.

Interfejs Async Stack Tagging API wprowadza nową metodę console o nazwie console.createTask(). Podpis interfejsu API 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żesz później użyć do uruchomienia treści zadania f.

// Task Creation
const task = console.createTask(name);

// Task Execution
task.run(f);

Zadanie tworzy połączenie między kontekstem, w którym zostało utworzone, a kontekstem wykonywanej funkcji asynchronicznej.

Po zastosowaniu do powyższej funkcji makeScheduler kod zmienia się w taki sposób:

function makeScheduler() {
  const tasks = [];

  return {
    schedule(f) {
      const task = console.createTask(f.name);
      tasks.push({ task, f });
    },

    work() {
      while (tasks.length) {
        const { task, f } = tasks.shift();
        task.run(f); // instead of f();
      }
    },
  };
}

Dzięki temu Narzędzia deweloperskie w Chrome mogą teraz wyświetlać lepszy zrzut stosu.

Zrzut stosu z wykonanym asynchronicznym kodem z informacjami o czasie jego zaplanowania.

Zwróć uwagę, że zrzut stosu zawiera teraz fragment businessLogic(). Oprócz tego zadanie ma znaną już nazwę someTask, a nie ogólną nazwę requestAnimationFrame.

Przyjazne ramki połączeń

Platformy często generują kod przy użyciu różnego rodzaju języków szablonów, na przykład Angular lub JSX, które zmieniają kod przypominający język HTML w zwykły kod JavaScript, który następnie działa w przeglądarce. Czasami tego rodzaju generowane funkcje mają nazwy, które nie są zbyt przyjazne – mogą to być nazwy jednoliterowe po zmniejszeniu albo nazwy mało znane lub nieznane, nawet jeśli takie nie są.

W przykładowym projekcie jest to na przykład AppComponent_Template_app_button_handleClick_1_listener, który widać w zrzucie stosu.

Zrzut ekranu zrzutu stosu z automatycznie wygenerowaną nazwą funkcji.

Aby rozwiązać ten problem, w Narzędziach deweloperskich w Chrome możesz teraz zmieniać nazwy tych funkcji za pomocą map źródeł. Jeśli mapa źródeł zawiera wpis nazwy na początku zakresu funkcji, ramka wywołania powinna wyświetlać tę nazwę w zrzucie stosu.

Jako autor nie musisz nic robić, aby włączyć to nowe zachowanie. Wprowadzenie tej zmiany należy do zasad.

Perspektywy

Dzięki funkcjom opisanym w tym poście Narzędzia deweloperskie w Chrome oferują lepsze możliwości debugowania. Zespół chce zbadać więcej obszarów. W szczególności dowiesz się, jak ulepszyć profilowanie w Narzędziach deweloperskich.

Zespół Narzędzi deweloperskich w Chrome zachęca twórców platform do wdrożenia tych nowych funkcji. Wskazówki dotyczące implementacji znajdziesz w studium przypadku: Better Angular Debugging with DevTools (w języku angielskim).