Depuração da Web moderna no Chrome DevTools

Introdução

Hoje, os autores podem usar muitas abstrações para criar aplicativos da Web. Em vez de interagir diretamente com as APIs de nível inferior fornecidas pela plataforma da Web, muitos autores usam frameworks, ferramentas de build e compiladores para criar aplicativos de um ponto de vista mais alto.

Por exemplo, os componentes criados com base no framework Angular são criados em TypeScript com modelos HTML. Por trás, o Angular CLI e o webpack compilam tudo para JavaScript e para um pacote, que é enviado ao navegador.

Ao depurar ou criar perfis de aplicativos da Web no DevTools, você pode conferir e depurar essa versão compilada do código, em vez do código que você realmente escreveu. Como autor, isso não é o que você quer:

  • Você não quer depurar o código JavaScript reduzido, mas sim o código JavaScript original.
  • Ao usar o TypeScript, você não quer depurar o JavaScript, mas sim o código TypeScript original.
  • Ao usar modelos como Angular, Lit ou JSX, nem sempre é recomendado depurar o DOM resultante. Talvez você queira depurar os componentes.

Em geral, é recomendável depurar seu próprio código conforme você o escreveu.

Embora os mapas de origem já reduzam essa lacuna, o Chrome DevTools e o ecossistema podem fazer mais nessa área.

Vamos dar uma olhada.

Código criado e código implantado

Atualmente, ao navegar pela árvore de arquivos no painel de fontes, você pode conferir o conteúdo do bundle compilado e, muitas vezes, minimizado. Esses são os arquivos que o navegador faz o download e executa. O DevTools chama isso de Código implantado.

Captura de tela da árvore de arquivos no Chrome DevTools mostrando o código implantado.

Isso não é muito prático e, muitas vezes, é difícil de entender. Como autor, você quer ver e depurar o código que escreveu, não o código implantado.

Para compensar, agora é possível mostrar o Código criado na árvore. Isso faz com que a árvore se pareça mais com os arquivos de origem que você encontra no ambiente de desenvolvimento integrado, e esses arquivos agora estão separados do Código implantado.

Captura de tela da árvore de arquivos no Chrome DevTools mostrando o código criado.

Para ativar essa opção no Chrome DevTools, acesse Configurações > Experimentos e marque Agrupar origens em árvores criadas e implantadas.

Captura de tela das configurações do DevTools.

"Apenas meu código"

Ao usar dependências ou criar com base em um framework, os arquivos de terceiros podem atrapalhar. Na maioria das vezes, você só quer ver seu código, não o de alguma biblioteca de terceiros escondida na pasta node_modules.

Para compensar, o DevTools tem uma configuração extra ativada por padrão: Adicionar automaticamente scripts conhecidos de terceiros à lista de ignorados. Ela está disponível em Ferramentas do desenvolvedor > Configurações > Lista de ignorados.

Captura de tela das configurações do DevTools.

Com essa configuração ativada, o DevTools oculta qualquer arquivo ou pasta que um framework ou ferramenta de build tenha marcado como para ignorar.

A partir da Angular v14.1.0, o conteúdo das pastas node_modules e webpack foi marcado como tal. Portanto, essas pastas, os arquivos nelas e outros artefatos de terceiros não aparecem em vários lugares nas Ferramentas do desenvolvedor.

Como autor, você não precisa fazer nada para ativar esse novo comportamento. Cabe ao framework implementar essa mudança.

Código na lista de ignorados em stack traces

Um lugar em que esses arquivos na lista de ignorados não aparecem mais é nos stack traces. Como autor, agora você vê stack traces mais relevantes.

Captura de tela de um stack trace no DevTools.

Se você quiser ver todos os frames de chamada do stack trace, clique no link Show more frames.

O mesmo se aplica às pilhas de chamadas que aparecem durante a depuração e a execução do código. Quando frameworks ou bundlers informam ao DevTools sobre scripts de terceiros, o DevTools oculta automaticamente todos os frames de chamada irrelevantes e pula qualquer código da lista de ignorados durante a depuração da etapa.

Captura de tela do depurador de origens do DevTools durante a depuração.

Código de lista de ignorados na árvore de arquivos

Para ocultar os arquivos e pastas da lista de ignorados da árvore de arquivos Código criado no painel Origens, marque Ocultar código da lista de ignorados na visualização de árvore de origens em Configurações > Experimentos nas Ferramentas do desenvolvedor.

Captura de tela das configurações do DevTools.

No projeto Angular de exemplo, as pastas node_modules e webpack agora estão ocultas.

Captura de tela da árvore de arquivos no Chrome DevTools mostrando o código criado, mas não o node_modules.

Código ignorado na lista do menu "Abrir rapidamente"

O código na lista de ignorados não é apenas oculto da árvore de arquivos, mas também do menu "Abrir rapidamente" (Control+P (Linux/Windows) ou Command+P (Mac)).

Captura de tela do DevTools com o menu "Abrir rapidamente".

Mais melhorias nos stack traces

Além dos relevantes, o Chrome DevTools apresenta ainda mais melhorias nos rastros de pilha.

Stack traces vinculados

Quando algumas operações são programadas para acontecer de forma assíncrona, os rastros de pilha no DevTools atualmente mostram apenas parte da história.

Por exemplo, aqui está um programador muito simples em um arquivo framework.js hipotético:

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

… e como um desenvolvedor pode usar isso no próprio código em um arquivo example.js:

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

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

businessLogic();

Ao adicionar um ponto de interrupção dentro do método someTask ou inspecionar o rastro impresso no console, você não vê nenhuma menção à chamada businessLogic() que foi a "causa raiz" dessa operação.

Em vez disso, você só vai ver a lógica de programação do framework que levou à execução da tarefa e nenhum breadcrumb no stack trace para ajudar a descobrir os links causais entre os eventos que levaram a essa tarefa.

Um stack trace de algum código executado de forma assíncrona sem informações sobre quando ele foi programado.

Graças a um novo recurso chamado "Marcação de pilha assíncrona", é possível contar toda a história vinculando as duas partes do código assíncrono.

A API Async Stack Tagging apresenta um novo método console chamado console.createTask(). A assinatura da API é a seguinte:

interface Console {
  createTask(name: string): Task;
}

interface Task {
  run<T>(f: () => T): T;
}

A chamada console.createTask() retorna uma instância Task que pode ser usada depois para executar o conteúdo f da tarefa.

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

// Task Execution
task.run(f);

A tarefa forma o vínculo entre o contexto em que foi criada e o contexto da função assíncrona que está sendo executada.

Aplicado à função makeScheduler acima, o código fica assim:

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();
      }
    },
  };
}

Graças a isso, o Chrome DevTools agora pode mostrar um stack trace melhor.

Um stack trace de algum código executado de forma assíncrona com informações sobre quando ele foi programado.

Observe como o businessLogic() agora está incluído no stack trace. Além disso, a tarefa tem um nome familiar, someTask, em vez do requestAnimationFrame genérico de antes.

Frames de chamada amigáveis

Os frameworks geralmente geram código de todos os tipos de linguagens de modelagem ao criar um projeto, como modelos Angular ou JSX que transformam código com aparência de HTML em JavaScript simples que é executado no navegador. Às vezes, esses tipos de funções geradas recebem nomes que não são muito amigáveis, como nomes de uma única letra após a minificação ou nomes obscuros ou desconhecidos, mesmo quando não são.

No projeto de exemplo, um exemplo disso é AppComponent_Template_app_button_handleClick_1_listener, que aparece no stack trace.

Captura de tela do stack trace com um nome de função gerado automaticamente.

Para resolver esse problema, o Chrome DevTools agora oferece suporte à renomeação dessas funções usando mapas de origem. Se um mapa de origem tiver uma entrada de nome para o início de um escopo de função, o frame de chamada precisará exibir esse nome no stack trace.

Como autor, você não precisa fazer nada para ativar esse novo comportamento. Cabe ao framework implementar essa mudança.

No futuro

Graças às adições destacadas nesta postagem, o Chrome DevTools pode oferecer uma melhor experiência de depuração. Há mais áreas que a equipe gostaria de explorar. Em particular, como melhorar a experiência de criação de perfil no DevTools.

A equipe do Chrome DevTools incentiva os autores da estrutura a adotar esses novos recursos. O Case Study: Better Angular Debugging with DevTools (link em inglês) oferece orientações sobre como implementar isso.