Nowoczesne debugowanie stron internetowych w Narzędziach deweloperskich w Chrome

Wprowadzenie

Obecnie autorzy mogą tworzyć swoje aplikacje internetowe na podstawie wielu abstrakcji. Zamiast bezpośrednio komunikować się z interfejsami API niższego poziomu, które są dostępne w ramach platformy internetowej, wielu autorów posługuje się platformami, narzędziami do kompilacji i kompilatorami, aby pisać swoje aplikacje z perspektywy wyższego poziomu.

Na przykład komponenty utworzone na platformie Angular są tworzone w języku TypeScript z użyciem szablonów HTML. Interfejs wiersza poleceń i pakiet internetowy Angular kompilują wszystko w języku JavaScript w tak zwany pakiet, który jest następnie wysyłany do przeglądarki.

Podczas debugowania lub profilowania aplikacji internetowych w Narzędziach deweloperskich można obecnie wyświetlić i debugować skompilowaną wersję kodu zamiast faktycznie napisanego kodu. Nie chcesz tego jednak robić jako autor:

  • Nie chcesz debugować zminifikowanego kodu JavaScript – wolisz debugować pierwotny kod JavaScript.
  • Jeśli używasz TypeScriptu, nie chcesz debugować JavaScriptu, tylko debugować oryginalny kod TypeScript.
  • Gdy używasz szablonów, np. Angular, Lit lub JSX, nie zawsze chcesz debugować wynikowy DOM. Możesz samodzielnie debugować komponenty.

Ogólnie rzecz biorąc, wolisz debugować własny kod w czasie, gdy go napisał.

Mapy źródłowe już w pewnym stopniu wypełniają tę lukę, ale Narzędzia deweloperskie w Chrome i cały ekosystem mają w tym obszarze większe możliwości.

Przyjrzyjmy się im bliżej.

Kod autorski a kod wdrożony

Obecnie podczas poruszania się po drzewie plików w panelu Źródła widać zawartość skompilowanego – i często zminimalizowanego – pakietu. Są to pliki pobierane i uruchamiane przez przeglądarkę. W Narzędziach deweloperskich jest to wdrożony kod.

Zrzut ekranu przedstawiający drzewo plików w Narzędziach deweloperskich w Chrome pokazujące wdrożony kod.

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

Aby to zrekompensować, możesz ustawić w drzewie Autorowany kod. Dzięki temu drzewo będzie bardziej przypominać pliki źródłowe, które widać w Twoim IDE. Pliki te zostaną teraz oddzielone od wdrożonego kodu.

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

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

Zrzut ekranu pokazujący ustawienia Narzędzi deweloperskich.

„Tylko mój kod”

Jeśli korzystasz z zależności lub tworzysz na platformie, pliki innych firm mogą Ci przeszkodzić. Zwykle chcesz widzieć tylko swój kod, a nie bibliotekę innej firmy, która znajduje się w folderze node_modules.

W Narzędziach deweloperskich domyślnie włączone jest dodatkowe ustawienie: Automatycznie dodawaj znane skrypty innych firm do listy ignorowanych. Znajdziesz je w DevTools. Ustawienia > Lista ignorowanych.

Zrzut ekranu pokazujący ustawienia Narzędzi deweloperskich.

Po włączeniu tego ustawienia Narzędzia deweloperskie ukrywają wszystkie pliki lub foldery, które 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 oznaczona w ten sposób. W związku z tym te foldery, zawarte w nich pliki i inne artefakty innych firm nie pojawiają się w różnych miejscach w Narzędziach deweloperskich.

Jako autor nie musisz nic robić, aby włączyć tę nową funkcję. Wdrożenie tej zmiany zależy od platformy.

Zignorowany kod w zrzutach stosu

Te ignorowane pliki nie są już widoczne w zrzutach stosu. Jako autor masz teraz dostęp do bardziej przydatnych zrzutów stosu.

Zrzut ekranu ze zrzutem stosu w Narzędziach deweloperskich.

Aby wyświetlić wszystkie ramki wywołań zrzutu stosu, zawsze możesz kliknąć link Pokaż więcej ramek.

To samo dotyczy stosów wywołań, które pojawiają się podczas debugowania i przeglądania kodu. Gdy platformy lub pakiety pakietów przekazują do Narzędzi deweloperskich informacje o skryptach innych firm, podczas debugowania krokowego narzędzia deweloperskie automatycznie ukrywają wszystkie nieistotne ramki wywołania i przeskakują nad kodem z listy zignorowanych.

Zrzut ekranu pokazujący debugera źródeł w Narzędziach deweloperskich podczas debugowania.

Kod z ignorowanego kodu w drzewie plików

Aby ukryć pliki i foldery znajdujące się na liście ignorowanych, w drzewie plików Authored Code w panelu Sources (Źródła) zaznacz opcję Ukryj ignorowany kod w widoku drzewa źródeł w sekcji Ustawienia > Eksperymenty w Narzędziach deweloperskich.

Zrzut ekranu pokazują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 zawierające informacje o autorze, ale bez parametru node_modules.

Zignorowany kod w menu „Szybkie otwieranie”

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

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

Kolejne ulepszenia zrzutów stosu

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

Zrzuty stosu połączonych

Jeśli niektóre operacje są zaplanowane asynchronicznie, zrzuty stosu w Narzędziach deweloperskich przedstawiają obecnie tylko część historii.

Oto 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();

...i jak deweloper może wykorzystać go we własnym kodzie w pliku example.js:

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

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

businessLogic();

Gdy dodajesz punkt przerwania w metodzie someTask lub sprawdzasz log czasu wydrukowany w konsoli, nie widzisz żadnych informacji o wywołaniu businessLogic(), które było „główną przyczyną” tej operacji.

Zamiast tego w zrzucie stosu widzisz tylko logikę planowania platformy, która doprowadziła do wykonania zadania, a w zrzucie stosu nie ma żadnych menu nawigacyjnego, które ułatwiają znalezienie powiązań przyczynowo-skutkowych między zdarzeniami prowadzącymi do tego zadania.

Zrzut stosu pewnego wykonanego kodu asynchronicznego bez informacji o tym, kiedy został zaplanowany.

Dzięki nowej funkcji o nazwie „Asynchroniczne tagowanie stosu” można przecież opowiedzieć wszystko na ten temat dzięki połączeniu ze sobą obu części kodu asynchronicznego.

W interfejsie Async Stack Tagging API wprowadziliśmy 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żna później użyć do uruchomienia zawartoś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.

Zastosowany do powyższej funkcji makeScheduler kod wygląda tak:

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 lepiej prezentować zrzut stosu.

Zrzut stosu niektórych wykonanego kodu asynchronicznego z informacjami o tym, kiedy został zaplanowany.

Zwróć uwagę, że w zrzucie stosu uwzględniana jest teraz funkcja businessLogic(). Co więcej, zadanie ma znajomą nazwę someTask zamiast ogólnej requestAnimationFrame jak wcześniej.

Przyjazne ramki wywołania

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 rodzaju generowane funkcje są nazywane nazwami, które nie są zbyt przyjazne. Mogą to być nazwy jednoliterowe po zminimalizacji albo niektóre niejasne lub nieznane nazwy, nawet jeśli nie są wcale.

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

Zrzut ekranu ze zrzutem 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ódłowych. Jeśli mapa źródłowa ma wpis z nazwą początku zakresu funkcji, ramka wywołania powinna wyświetlić tę nazwę w zrzucie stosu.

Jako autor nie musisz nic robić, aby włączyć tę nową funkcję. Wdrożenie tej zmiany zależy od platformy.

Perspektywy

Dzięki dodatkom omówionym w tym poście możesz zwiększyć komfort debugowania w Narzędziach deweloperskich w Chrome. Nasz zespół chciałby dowiedzieć się więcej. W szczególności chodzi o ulepszanie profilowania w Narzędziach deweloperskich.

Zespół Narzędzi deweloperskich w Chrome zachęca twórców platformy do wdrożenia tych nowych możliwości. Wskazówki wdrożenia tego rozwiązania znajdziesz w artykule Case Study: Better Angular Debugging with DevTools (Studium przypadku: lepsze debugowanie Angular za pomocą Narzędzi deweloperskich).