Un'esperienza di debug migliorata
Negli ultimi mesi il team Chrome DevTools ha collaborato con il team Angular per introdurre miglioramenti all'esperienza di debug in Chrome DevTools. I membri di entrambi i team hanno collaborato e hanno intrapreso delle azioni per consentire agli sviluppatori di eseguire il debug e la profilazione delle applicazioni web dal punto di vista dell'autore, in termini di lingua di origine e struttura del progetto, con accesso a informazioni familiari e pertinenti.
Questo post offre un'occhiata dietro le quinte per scoprire quali modifiche in Angular e Chrome DevTools sono state necessarie per ottenere questo risultato. Anche se alcune di queste modifiche vengono dimostrate tramite Angular, possono essere applicate anche ad altri framework. Il team di Chrome DevTools incoraggia altri framework ad adottare le nuove API della console e i punti di estensione delle mappe di origine, in modo che anch'essi possano offrire una migliore esperienza di debug agli utenti.
Codice di ignoramento della scheda
Durante il debug di applicazioni utilizzando Chrome DevTools, gli autori in genere vogliono vedere solo solo il loro codice, non quello del framework sottostante o alcune dipendenze nascoste nella cartella node_modules
.
Per raggiungere questo obiettivo, il team di DevTools ha introdotto un'estensione per le mappe di origine, chiamata x_google_ignoreList
. Questa estensione viene utilizzata per identificare origini di terze parti, come il codice del framework o il codice generato dal bundler. Quando un framework utilizza questa estensione, gli autori ora evitano automaticamente il codice che non vogliono vedere o che non vogliono vedere senza doverlo configurare manualmente in anticipo.
In pratica, Chrome DevTools può nascondere automaticamente il codice identificato come tale nelle tracce dello stack, nella struttura ad albero Origini, nella finestra di dialogo Apri rapidamente e migliorare anche il comportamento del passaggio e della ripresa nel debugger.
L'estensione della mappa di origine x_google_ignoreList
Nelle mappe di origine, il nuovo campo x_google_ignoreList
fa riferimento all'array sources
ed elenca gli indici di tutte le origini di terze parti note nella mappa di origine. Durante l'analisi della mappa sorgente, Chrome DevTools la utilizzerà per capire quali sezioni del codice devono essere inserite nella lista ignora.
Di seguito è riportata una mappa di origine per un file generato out.js
. Esistono due elementi sources
originali che hanno contribuito a generare il file di output: foo.js
e lib.js
. Il primo è un codice scritto da uno sviluppatore di siti web, mentre il secondo è un framework utilizzato.
{
"version" : 3,
"file": "out.js",
"sourceRoot": "",
"sources": ["foo.js", "lib.js"],
"sourcesContent": ["...", "..."],
"names": ["src", "maps", "are", "fun"],
"mappings": "A,AAAB;;ABCDE;"
}
sourcesContent
è incluso per entrambe le origini originali e Chrome DevTools mostra questi file per impostazione predefinita in Debugger:
- Come file nella struttura ad albero Origini.
- Come risultati nella finestra di dialogo Apri rapidamente.
- Come posizioni del frame di chiamata mappate nelle tracce dello stack di errori quando è in pausa su un breakpoint e durante l'esecuzione passo passo.
Ora c'è un'altra informazione che può essere inclusa nelle mappe di origine per identificare quale di queste origini è codice proprietario o di terze parti:
{
...
"sources": ["foo.js", "lib.js"],
"x_google_ignoreList": [1],
...
}
Il nuovo campo x_google_ignoreList
contiene un singolo indice che fa riferimento all'array sources
: 1. Questo specifica che le regioni mappate a lib.js
sono in realtà codice di terze parti che deve essere aggiunto automaticamente all'elenco di ignorati.
In un esempio più complesso, mostrato di seguito, gli indici 2, 4 e 5 specificano che le regioni mappate a lib1.ts
, lib2.coffee
e hmr.js
sono tutte codice di terze parti che deve essere aggiunto automaticamente all'elenco di ignorati.
{
...
"sources": ["foo.html", "bar.css", "lib1.ts", "baz.js", "lib2.coffee", "hmr.js"],
"x_google_ignoreList": [2, 4, 5],
...
}
Se sei uno sviluppatore di framework o bundler, assicurati che le mappe di origine generate durante il processo di compilazione includano questo campo per collegarti a queste nuove funzionalità in DevTools di Chrome.
x_google_ignoreList
in Angular
A partire dalla versione 14.1.0 di Angular, i contenuti delle cartelle node_modules
e webpack
sono stati contrassegnati come "da ignorare".
Ciò è stato ottenuto attraverso una modifica a angular-cli
creando un plug-in che si aggancia al modulo Compiler
di webpack
Il plug-in webpack creato dai nostri tecnici si collega allo stage PROCESS_ASSETS_STAGE_DEV_TOOLING
e compila il campo x_google_ignoreList
nelle mappe di origine per gli asset finali generati da Webpack e caricati dal browser.
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)));
Analisi dello stack collegate
Le tracce dello stack rispondono alla domanda "come ci sono arrivato", ma spesso questo avviene dal punto di vista della macchina e non necessariamente corrisponde al punto di vista dello sviluppatore o al suo modello mentale del runtime dell'applicazione. Ciò è particolarmente vero quando alcune operazioni sono pianificate per essere eseguite in modo asincrono più tardi: potrebbe comunque essere interessante conoscere la "causa principale" o il lato della pianificazione di queste operazioni, ma è esattamente un aspetto che non farà parte di un'analisi dello stack asincrona.
V8 ha internamente un meccanismo per tenere traccia di queste attività asincrone quando vengono utilizzate le primitive di pianificazione del browser standard, ad esempio setTimeout
. In questi casi viene eseguita per impostazione predefinita, quindi gli sviluppatori possono già esaminarla. Tuttavia, in progetti più complessi non è così semplice, soprattutto se si utilizza un framework con meccanismi di pianificazione più avanzati, ad esempio uno che esegue il monitoraggio delle zone, l'inserimento in coda di attività personalizzate o che suddivide gli aggiornamenti in più unità di lavoro eseguite nel tempo.
Per risolvere il problema, DevTools espone un meccanismo chiamato "API di tagging dello stack asincrono" nell'oggetto console
, che consente agli sviluppatori di framework di suggerire sia le posizioni in cui le operazioni sono pianificate sia dove vengono eseguite.
L'API Async Stack Tagging
Senza il tagging dello stack asincrono, le analisi dello stack per il codice eseguito in modo asincrono in modi complessi dai framework vengono visualizzate senza alcuna connessione al codice in cui è stato pianificato.
Con il tagging dello stack asincrono è possibile fornire questo contesto e l'analisi dello stack ha il seguente aspetto:
Per farlo, utilizza un nuovo metodo console
denominato console.createTask()
fornito dall'API Async Stack Tagging. La firma è la seguente:
interface Console {
createTask(name: string): Task;
}
interface Task {
run<T>(f: () => T): T;
}
La chiamata di console.createTask()
restituisce un'istanza Task
che puoi utilizzare in un secondo momento per eseguire il codice asincrono.
// Task Creation
const task = console.createTask(name);
// Task Execution
task.run(f);
Le operazioni asincrone possono anche essere nidificate e le "cause principali" verranno visualizzate nella traccia dello stack in sequenza.
Le attività possono essere eseguite un numero qualsiasi di volte e il payload di lavoro può variare da un'esecuzione all'altra. Lo stack di chiamate nel sito di pianificazione verrà memorizzato fino a quando l'oggetto dell'attività non verrà garbage collection.
L'API Async Stack Tagging in Angular
In Angular sono state apportate modifiche a NgZone, il contesto di esecuzione di Angular che persiste tra le attività asincrone.
Quando pianifica un'attività, utilizza console.createTask()
se disponibile. L'istanza Task
risultante viene archiviata per un uso futuro. All'attivazione dell'attività, NgZone utilizzerà l'istanza Task
archiviata per eseguirla.
Queste modifiche sono state implementate in NgZone 0.11.8 di Angular tramite le richieste pull #46693 e #46958.
Cornici di chiamata amichevoli
Durante la creazione di un progetto, i framework spesso generano codice da tutti i tipi di linguaggi di modelli, come i modelli Angular o JSX, che trasformano il codice HTML in semplice JavaScript che poi viene eseguito nel browser. A volte, a questi tipi di funzioni generate vengono assegnati nomi non molto intuitivi, ad esempio nomi di una sola lettera dopo la minimizzazione o nomi oscuri o non familiari anche se non lo sono.
In Angular non è raro vedere frame di chiamata con nomi come AppComponent_Template_app_button_handleClick_1_listener
nelle tracce dello stack.
Per risolvere il problema, Chrome DevTools ora supporta la ridenominazione di queste funzioni tramite le mappe di origine. Se una mappa di origine contiene una voce di nome per l'inizio dell'ambito di una funzione (ovvero la parentesi tonda sinistra dell'elenco dei parametri), lo stack frame deve mostrare quel nome nella traccia dello stack.
Frame di chiamata amichevoli in Angular
La ridenominazione dei frame di chiamata in Angular è un impegno continuo. Prevediamo che questi miglioramenti verranno implementati gradualmente nel tempo.
Durante l'analisi dei modelli HTML scritti dagli autori, il compilatore Angular genera codice TypeScript, che viene infine transpiled in codice JavaScript caricato ed eseguito dal browser.
Nell'ambito di questa procedura di generazione di codice vengono create anche le mappe di origine. Stiamo attualmente esplorando modi per includere i nomi delle funzioni nel campo "nomi" delle mappe di origine e fare riferimento a questi nomi nelle mappature tra il codice generato e il codice originale.
Ad esempio, se viene generata una funzione per un listener di eventi e il suo nome non è semplice o è stato rimosso durante la minimizzazione, le mappe di origine possono ora includere il nome più semplice per questa funzione nel campo "names" e la mappatura per l'inizio dell'ambito della funzione può ora fare riferimento a questo nome (ovvero la parentesi aperta dell'elenco di parametri). Chrome DevTools utilizzerà questi nomi per rinominare i frame di chiamata nelle tracce dello stack.
Prospettive future
Utilizzare Angular come test pilota per verificare il nostro lavoro è stata un'esperienza meravigliosa. Ci piacerebbe conoscere gli sviluppatori di framework e fornire un feedback su questi punti di estensione.
Ci sono altre aree che vorremmo esplorare. In particolare, come migliorare l'esperienza di profilazione in DevTools.