API Long Animation Frames

A API Long Animation Frames (LoAF, pronunciada como "Lo-Af") é uma atualização da API Long Tasks para entender melhor as atualizações lentas da interface do usuário (IU). Isso pode ser útil para identificar frames de animação lentos que provavelmente afetam a métrica Core Web Vital Interaction to Next Paint (INP), que mede a capacidade de resposta, ou para identificar outros problemas de instabilidade da interface que afetam a fluidez.

Status da API

Compatibilidade com navegadores

  • Chrome: 123.
  • Edge: 123.
  • Firefox: não é compatível.
  • Safari: não é compatível.

Origem

Após um teste de origem do Chrome 116 para o Chrome 122, a API LoAF foi lançada no Chrome 123.

Segundo plano: a API Long Tasks

Compatibilidade com navegadores

  • Chrome: 58.
  • Edge: 79.
  • Firefox: não é compatível.
  • Safari: não é compatível.

Origem

A API Long Animation Frames é uma alternativa à API Long Tasks, que está disponível no Chrome há algum tempo (desde o Chrome 58). Como o nome sugere, a API Long Task permite monitorar tarefas longas, que ocupam a linha de execução principal por 50 milissegundos ou mais. As tarefas longas podem ser monitoradas usando a interface PerformanceLongTaskTiming com um PeformanceObserver:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

Tarefas longas podem causar problemas de resposta. Se um usuário tentar interagir com uma página, por exemplo, clicar em um botão ou abrir um menu, mas a linha de execução principal já estiver lidando com uma tarefa demorada, a interação do usuário será adiada até que a tarefa seja concluída.

Para melhorar a capacidade de resposta, é recomendável dividir tarefas longas. Se cada tarefa longa for dividida em uma série de tarefas menores, talvez seja possível executar tarefas mais importantes entre elas para evitar atrasos significativos na resposta às interações.

Portanto, ao tentar melhorar a capacidade de resposta, o primeiro esforço geralmente é executar um rastro de desempenho e analisar tarefas longas. Isso pode ser feito com uma ferramenta de auditoria baseada em laboratório, como o Lighthouse (que tem uma auditoria Evitar tarefas longas da linha de execução principal) ou analisando tarefas longas no Chrome DevTools.

Os testes baseados em laboratório geralmente não são um bom ponto de partida para identificar problemas de responsividade, porque essas ferramentas podem não incluir interações. Quando incluem, são um pequeno subconjunto de interações prováveis. O ideal é medir as causas das interações lentas no campo.

Desvantagens da API Long Tasks

Medir tarefas longas no campo usando um Performance Observer é apenas um pouco útil. Na realidade, ele não fornece muitas informações além do fato de que uma tarefa demorada aconteceu e quanto tempo ela levou.

As ferramentas de monitoramento de usuários reais (RUM) costumam usar isso para gerar tendências no número ou na duração de tarefas longas ou identificar em quais páginas elas ocorrem. No entanto, sem os detalhes subjacentes do que causou a tarefa longa, isso é apenas de uso limitado. A API Long Tasks tem apenas um modelo de atribuição básico, que, na melhor das hipóteses, informa apenas o contêiner em que a tarefa demorada ocorreu (o documento de nível superior ou um <iframe>), mas não o script ou a função que a chamou, conforme mostrado em uma entrada típica:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

A API Long Tasks também é uma visualização incompleta, já que pode excluir algumas tarefas importantes. Algumas atualizações, como a renderização, acontecem em tarefas separadas que, idealmente, precisam ser incluídas com a execução anterior que causou essa atualização para medir com precisão o "trabalho total" dessa interação. Para mais detalhes sobre as limitações de depender de tarefas, consulte a seção "Onde as tarefas longas ficam aquém" do explicativo.

O último problema é que a medição de tarefas longas só informa sobre tarefas individuais que levam mais tempo do que o limite de 50 milissegundos. Um frame de animação pode ser composto por várias tarefas menores que esse limite de 50 milissegundos, mas ainda assim bloquear coletivamente a capacidade de renderização do navegador.

A API Long Animation Frames

Compatibilidade com navegadores

  • Chrome: 123.
  • Edge: 123.
  • Firefox: não é compatível.
  • Safari: não é compatível.

Origem

A API Long Animation Frames (LoAF, na sigla em inglês) é uma nova API que busca resolver algumas das deficiências da API Long Tasks para permitir que os desenvolvedores tenham insights mais úteis para ajudar a resolver problemas de capacidade de resposta e melhorar a INP, além de receber insights sobre problemas de fluidez.

Uma boa capacidade de resposta significa que a página responde rapidamente às interações feitas com ela. Isso envolve a capacidade de pintar todas as atualizações necessárias pelo usuário em tempo hábil e evitar o bloqueio dessas atualizações. Para INP, recomendamos responder em 200 milissegundos ou menos, mas para outras atualizações (por exemplo, animações), até mesmo 200 milissegundos pode ser muito tempo.

A API Long Animation Frames é uma abordagem alternativa para medir o trabalho de bloqueio. Em vez de medir as tarefas individuais, a API Long Animation Frames, como o nome sugere, mede frames de animação longos. Um frame de animação longo é quando uma atualização de renderização é atrasada por mais de 50 milissegundos (o mesmo que o limite para a API Long Tasks).

Os frames de animação longos são medidos a partir do início das tarefas que exigem renderização. Quando a primeira tarefa em um frame de animação longa não requer renderização, o frame de animação longa é encerrado após a conclusão da tarefa sem renderização, e um novo frame de animação longa é iniciado com a próxima tarefa. Esse frame de animação longa sem renderização ainda é incluído na API Long Animation Frames quando é maior que 50 milissegundos (com um tempo de renderStart de 0) para permitir a medição de trabalho potencialmente bloqueado.

Os frames de animação longos podem ser observados de maneira semelhante a tarefas longas com um PerformanceObserver, mas observando o tipo long-animation-frame:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Os frames de animação longos anteriores também podem ser consultados na Linha do tempo de desempenho desta forma:

const loafs = performance.getEntriesByType('long-animation-frame');

No entanto, há um maxBufferSize para entradas de performance, depois que as entradas mais recentes são descartadas. Portanto, a abordagem PerformanceObserver é a recomendada. O tamanho do buffer long-animation-frame é definido como 200, o mesmo que para long-tasks.

Vantagens de analisar frames em vez de tarefas

A principal vantagem de analisar isso de uma perspectiva de frame, e não de tarefas, é que uma animação longa pode ser composta por qualquer número de tarefas que resultaram cumulativamente em um frame de animação longo. Isso aborda o ponto final mencionado anteriormente, em que a soma de muitas tarefas menores que bloqueiam a renderização antes de um frame de animação pode não ser exibida pela API Long Tasks.

Outra vantagem dessa visualização alternativa em tarefas longas é a capacidade de fornecer detalhes de tempo de todo o frame. Em vez de incluir apenas um startTime e um duration, como a API Long Tasks, o LoAF inclui um detalhamento muito mais detalhado das várias partes da duração do frame.

Carimbos de data/hora e durações de frames

  • startTime: o horário de início do frame de animação longa em relação ao horário de início da navegação.
  • duration: a duração do frame de animação longo (sem incluir o tempo de apresentação).
  • renderStart: o horário de início do ciclo de renderização, que inclui callbacks requestAnimationFrame, cálculo de estilo e layout, callbacks de observador de redimensionamento e de interseção.
  • styleAndLayoutStart: o início do período gasto em cálculos de estilo e layout.
  • firstUIEventTimestamp: o tempo do primeiro evento de interface (mouse/teclado e assim por diante) a ser processado durante o curso deste frame.
  • blockingDuration: a duração total em milissegundos em que o frame de animação bloqueia o processamento de entrada ou outras tarefas de alta prioridade.

Uma explicação de blockingDuration

Um frame de animação longo pode ser composto por várias tarefas. O blockingDuration é a soma das durações de tarefas com mais de 50 milissegundos (incluindo a duração de renderização final na tarefa mais longa).

Por exemplo, se um frame de animação longo for composto por duas tarefas de 55 e 65 milissegundos, seguido de uma renderização de 20 milissegundos, o duration será de aproximadamente 140 milissegundos com um blockingDuration de (55 - 50) + (65 + 20 - 50) = 40 milissegundos. Por 40 milissegundos durante esse frame de animação de 140 milissegundos, o frame foi considerado bloqueado para processar a entrada.

Se você quer analisar duration ou blockingDuration

Para a exibição comum de 60 hertz, um navegador tenta programar um frame pelo menos a cada 16,66 milissegundos (para garantir atualizações suaves) ou após uma tarefa de alta prioridade, como o processamento de entrada (para garantir atualizações responsivas). No entanto, se não houver entrada nem outras tarefas de alta prioridade, mas houver uma fila de outras tarefas, o navegador normalmente continuará o frame atual bem depois de 16,66 milissegundos, não importa o quanto as tarefas estejam divididas. Ou seja, o navegador sempre tenta priorizar as entradas, mas pode escolher resolver uma fila de tarefas em vez de atualizações de renderização. Isso ocorre porque a renderização é um processo caro. Portanto, o processamento de uma tarefa de renderização combinada para várias tarefas geralmente leva a uma redução geral do trabalho.

Portanto, frames de animação longos com um blockingDuration baixo ou zero ainda precisam parecer responsivos à entrada. Portanto, reduzir ou eliminar blockingDuration quebrando tarefas longas é fundamental para melhorar a capacidade de resposta medida pelo INP.

No entanto, muitos frames de animação longos, independentemente de blockingDuration, indicam atualizações da interface atrasadas e, portanto, ainda podem afetar a fluidez e causar a sensação de uma interface de usuário lenta para rolagem ou animações, mesmo que essas sejam menos um problema para a capacidade de resposta medida pelo INP. Para entender os problemas nessa área, consulte o duration, mas eles podem ser mais difíceis de otimizar, já que não é possível resolver isso dividindo o trabalho, mas sim reduzindo-o.

Tempos de frame

Os carimbos de data/hora mencionados anteriormente permitem que o frame de animação longa seja dividido em tempos:

Tempo Cálculo
Horário de início startTime
Horário de término startTime + duration
Duração do trabalho renderStart ? renderStart - startTime : duration
Duração da renderização renderStart ? (startTime + duration) - renderStart: 0
Renderização: duração do pré-layout styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Renderização: duração do estilo e do layout styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Melhor atribuição de script

O tipo de entrada long-animation-frame inclui dados de atribuição melhores de cada script que contribuiu para um frame de animação longo (para scripts com mais de 5 milissegundos).

Assim como na API Long Tasks, isso será fornecido em uma matriz de entradas de atribuição, cada uma com os seguintes detalhes:

  • Um name e um EntryType vão retornar script.
  • Um invoker significativo, indicando como o script foi chamado (por exemplo, 'IMG#id.onload', 'Window.requestAnimationFrame' ou 'Response.json.then').
  • O invokerType do ponto de entrada do script:
    • user-callback: um callback conhecido registrado em uma API da plataforma da Web (por exemplo, setTimeout, requestAnimationFrame).
    • event-listener: um listener de um evento da plataforma (por exemplo, click, load, keyup).
    • resolve-promise: manipulador de uma promessa de plataforma (por exemplo, fetch()). No caso de promessas, todos os manipuladores das mesmas promessas são misturados como um "script".).
    • reject-promise: conforme resolve-promise, mas para a rejeição.
    • classic-script: avaliação do script (por exemplo, <script> ou import())
    • module-script: igual a classic-script, mas para scripts de módulo.
  • Separe os dados de tempo desse script:
    • startTime: hora em que a função de entrada foi invocada.
    • duration: a duração entre startTime e o término do processamento da próxima fila de microtarefas.
    • executionStart: o tempo após a compilação.
    • forcedStyleAndLayoutDuration: o tempo total gasto no processamento de layout e estilo forçados dentro dessa função (consulte thrashing).
    • pauseDuration: tempo total gasto "pausando" operações síncronas (alerta, XHR síncrono).
  • Detalhes da origem do script:
    • sourceURL: o nome do recurso do script, quando disponível (ou vazio se não encontrado).
    • sourceFunctionName: o nome da função do script, quando disponível (ou vazio se não encontrado).
    • sourceCharPosition: a posição do caractere do script, quando disponível (ou -1 se não encontrado).
  • windowAttribution: o contêiner (o documento de nível superior ou um <iframe>) em que o frame de animação longo ocorreu.
  • window: uma referência à janela de mesma origem.

Quando fornecidas, as entradas de origem permitem que os desenvolvedores saibam exatamente como cada script no frame de animação longa foi chamado, até a posição do caractere no script de chamada. Isso fornece o local exato em um recurso JavaScript que resultou no frame de animação longo.

Exemplo de uma entrada de performance long-animation-frame

Um exemplo completo de entrada de performance long-animation-frame, que contém um único script, é:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

Isso fornece uma quantidade sem precedentes de dados para que os sites possam entender a causa das atualizações de renderização lentas.

Usar a API Long Animation Frames no campo

Ferramentas como o Chrome DevTools e o Lighthouse, embora úteis para descobrir e reproduzir problemas, são ferramentas de laboratório que podem perder aspectos importantes da experiência do usuário que só os dados de campo podem fornecer.

A API Long Animation Frames foi projetada para ser usada no campo para coletar dados contextuais importantes sobre interações do usuário que a API Long Tasks não conseguia. Isso pode ajudar a identificar e reproduzir problemas de interatividade que você não teria descoberto de outra forma.

Recurso de detecção de suporte da API Long Animation Frames

Use o código abaixo para testar se a API tem suporte:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

O caso de uso mais óbvio da API Long Animation Frames é ajudar a diagnosticar e corrigir problemas de Interaction to Next Paint (INP), e esse foi um dos principais motivos pelos quais a equipe do Chrome desenvolveu essa API. Uma INP boa é quando todas as interações são respondidas em 200 milissegundos ou menos desde a interação até que o frame seja pintado. Como a API Long Animation Frames mede todos os frames que levam 50 ms ou mais, a maioria das INPs problemáticas precisa incluir dados de LoAF para ajudar a diagnosticar essas interações.

O "LoAF de entrada" é o LoAF que inclui a interação de entrada, conforme mostrado no diagrama a seguir:

Exemplos de frames de animação longos em uma página, com o INP LoAF destacado.
Uma página pode ter muitas LoAFs, e uma delas está relacionada à interação INP.

Em alguns casos, é possível que um evento INP abranja duas LoAFs, normalmente se a interação acontecer depois que o frame tiver iniciado a parte de renderização do frame anterior. Assim, o manipulador de eventos é processado no próximo frame:

Exemplos de frames de animação longos em uma página, com o INP LoAF destacado.
Uma página pode ter muitas LoAFs, e uma delas está relacionada à interação INP.

Em raras circunstâncias, é possível que ele se estenda por mais de duas LoAFs.

Ao registrar os dados de LoAF associados à interação de INP, você consegue mais informações sobre a interação de INP para ajudar a diagnosticá-la. Isso é útil principalmente para entender o delay de entrada, porque você pode conferir quais outros scripts estavam em execução naquele frame.

Também pode ser útil entender a duração do processamento e o atraso na apresentação se os manipuladores de eventos não estiverem reproduzindo os valores encontrados, já que outros scripts podem estar sendo executados para os usuários e não incluídos nos seus testes.

Não há nenhuma API direta para vincular uma entrada de INP à entrada ou entradas de LoAF relacionadas, mas é possível fazer isso no código comparando os horários de início e de término de cada uma (consulte o script de exemplo WhyNp). A biblioteca web-vitals inclui todos os LoAFs que se cruzam na propriedade longAnimationFramesEntries da interface de atribuição de INP da v4.

Depois de vincular a entrada ou as entradas do LoAF, você pode incluir informações com a atribuição de INP. O objeto scripts contém algumas das informações mais valiosas, já que pode mostrar o que mais estava sendo executado nesses frames. Assim, o envio desses dados de volta ao serviço de análise permite entender melhor por que as interações foram lentas.

Relatar LoAFs para a interação de INP é uma boa maneira de descobrir quais são os problemas de interatividade mais urgentes na sua página. Cada usuário pode interagir de maneira diferente com sua página, e com um volume suficiente de dados de atribuição de INP, vários problemas potenciais serão incluídos nos dados de atribuição de INP. Assim, você pode classificar os scripts por volume para saber quais estão correlacionados com a INP lenta.

Informar dados de animação mais longos de volta a um endpoint de análise

Uma desvantagem de olhar apenas as LoAFs de INP é que você pode perder outras áreas potenciais de melhorias que podem causar problemas futuros de INP. Isso pode levar a uma sensação de perseguição, em que você corrige um problema de INP esperando uma grande melhoria, mas descobre que a próxima interação mais lenta é apenas um pouco melhor do que isso, então sua INP não melhora muito.

Em vez de analisar apenas o LoAF do INP, considere todos os LoAFs durante a vida útil da página:

Uma página com muitos LoAFs, alguns dos quais acontecem durante interações, mesmo que não sejam interações do INP.
A análise de todos os LoAFs pode ajudar a identificar problemas futuros de INP.

No entanto, cada entrada de LoAF contém dados consideráveis. Portanto, é recomendável restringir sua análise a apenas algumas LoAFs. Além disso, como as entradas de frames de animação longa podem ser muito grandes, os desenvolvedores precisam decidir quais dados da entrada devem ser enviados para a análise. Por exemplo, os tempos de resumo da entrada e talvez os nomes dos scripts ou algum outro conjunto mínimo de outros dados contextuais que possam ser considerados necessários.

Alguns padrões sugeridos para reduzir a quantidade de dados de frames de animação longa incluem:

Qual desses padrões funciona melhor para você depende de quão longe você está na jornada de otimização e de quão comuns são os frames de animação longos. Para um site que nunca foi otimizado para responsividade, pode haver muitos LoAFs. Você pode limitar a apenas LoAFs com interações, definir um limite alto ou analisar apenas os piores.

À medida que você resolve os problemas comuns de capacidade de resposta, é possível ampliar o alcance, não se limitando apenas a interações ou durações de bloqueio altas, ou reduzindo os limites.

Observar frames de animação longos com interações

Para ter insights além do frame de animação longa da INP, você pode analisar todos os LoAFs com interações (que podem ser detectadas pela presença de um valor firstUIEventTimestamp) com um blockingDuration alto.

Esse também pode ser um método mais fácil de monitorar os LoAFs de INP em vez de tentar correlacionar os dois, o que pode ser mais complexo. Na maioria dos casos, isso inclui o LoAF de INP para uma determinada visita. Em casos raros, quando isso não acontece, ainda são mostradas interações longas que são importantes de corrigir, já que podem ser a interação de INP para outros usuários.

O código a seguir registra todas as entradas de LoAF com um blockingDuration maior que 100 milissegundos em que uma interação ocorreu durante o frame. O valor 100 foi escolhido aqui porque é menor que o limite de INP "bom" de 200 milissegundos. Você pode escolher um valor maior ou menor, dependendo das suas necessidades.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > REPORTING_THRESHOLD_MS &&
      entry.firstUIEventTimestamp > 0
    ) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Observe frames de animação longos com durações de bloqueio altas

Para melhorar a análise de todos os frames de animação longos com interações, você pode analisar todos os frames de animação longos com durações de bloqueio altas. Isso indica possíveis problemas de INP se um usuário interagir durante esses frames de animação longos.

O código a seguir registra todas as entradas de LoAF com uma duração de bloqueio maior que 100 milissegundos em que uma interação ocorreu durante o frame. O valor 100 foi escolhido porque é menor que o limite "bom" de INP de 200 milissegundos para ajudar a identificar possíveis frames problemáticos, mantendo a quantidade de frames de animação longa informados no mínimo. Você pode escolher um valor maior ou menor, dependendo das suas necessidades.

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.blockingDuration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Observe frames de animação longos durante atualizações críticas da interface para melhorar a fluidez.

Como mencionado anteriormente, analisar frames de animação de alta duração com bloqueio pode ajudar a resolver a capacidade de resposta de entrada. No entanto, para garantir avidez, você precisa analisar todos os frames de animação longos com um duration longo.

Como isso pode gerar bastante ruído, é recomendável restringir as medições a pontos-chave com um padrão como este:

const REPORTING_THRESHOLD_MS = 100;

const observer = new PerformanceObserver(list => {
  if (measureImportantUIupdate) {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

async function doUIUpdatesWithMeasurements() {
  measureImportantUIupdate = true;
  await doUIUpdates();
  measureImportantUIupdate = false;
}

Observe os piores frames de animação longa

Em vez de ter um limite definido, os sites podem querer coletar dados sobre o frame de animação mais longo (ou frames) para reduzir o volume de dados que precisam ser beaconizados. Portanto, não importa quantos frames de animação longa uma página tenha, apenas os dados dos piores, cinco, dez ou quantos frames de animação longa forem absolutamente necessários, são enviados de volta.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Essas estratégias também podem ser combinadas. Analise apenas os 10 piores LoAFs, com interações mais longas que 100 milissegundos.

No momento apropriado (de preferência no evento visibilitychange), o beacon volta ao Google Analytics. Para testes locais, use console.table periodicamente:

console.table(longestBlockingLoAFs);

Identificar padrões comuns em frames de animação longos

Uma estratégia alternativa seria analisar os scripts comuns que aparecem com mais frequência nas entradas de frames de animação longa. Os dados podem ser informados no nível do script e da posição do caractere para identificar os infratores recorrentes.

Isso pode funcionar muito bem para plataformas personalizáveis, em que temas ou plug-ins que causam problemas de desempenho podem ser identificados em vários sites.

O tempo de execução de scripts comuns ou de origem de terceiros em frames de animação longos pode ser resumido e informado para identificar os contribuintes comuns de frames de animação longos em um site ou em uma coleção de sites. Por exemplo, para analisar URLs:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

Um exemplo dessa saída é:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

Usar a API Long Animation Frames nas ferramentas

A API também permite ferramentas adicionais para depuração local. Embora algumas ferramentas, como o Lighthouse e o Chrome DevTools, tenham conseguido coletar muitos desses dados usando detalhes de rastreamento de nível inferior, ter essa API de nível mais alto pode permitir que outras ferramentas acessem esses dados.

Mostrar dados de frames de animação longos no DevTools

É possível mostrar frames de animação longos no DevTools usando a API performance.measure(), que são mostrados na faixa de tempos do usuário do DevTools em rastros de desempenho para mostrar onde concentrar seus esforços para melhorias de desempenho. Usando a API de extensibilidade do DevTools, elas podem até ser mostradas na própria faixa:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
      detail: {
        devtools: {
          dataType: "track-entry",
          track: "Long animation frames",
          trackGroup: "Performance Timeline",
          color: "tertiary-dark",
          tooltipText: 'LoAF'
        }
      }
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });
Rastreamento do painel de desempenho das DevTools com uma faixa personalizada mostrando dados de frame de animação longa que podem ser comparados ao gráfico de chama principal.
Mostrando dados de frame de animação longos no DevTools.

A longo prazo, os frames de animação longos provavelmente serão incorporados ao próprio DevTools, mas o snippet de código anterior permite que eles sejam exibidos nesse meio tempo.

A primeira entrada na figura anterior também demonstra onde o navegador processou várias tarefas juntas no mesmo frame de animação longo, em vez de renderizá-las entre elas. Como mencionado anteriormente, isso pode acontecer quando não há tarefas de entrada de alta prioridade, mas há uma fila de tarefas. A primeira tarefa longa tem algumas atualizações de renderização para concluir. Caso contrário, o frame de animação longa atual seria redefinido depois e um novo seria iniciado com a próxima tarefa. No entanto, em vez de acionar esse render imediatamente, o navegador processou várias tarefas adicionais e só depois acionou a tarefa de renderização longa e encerrou o frame de animação longa. Isso demonstra a utilidade de analisar frames de animação longos no DevTools, em vez de apenas tarefas longas, para ajudar a identificar renderizações atrasadas.

Usar dados de frames de animação longa em outras ferramentas para desenvolvedores

A extensão das Core Web Vitals mostrou o valor nas informações de depuração do resumo de geração de registros para diagnosticar problemas de performance.

Agora, ele também mostra dados de frame de animação longa para cada callback de INP e cada interação:

Geração de registros do console da extensão &quot;Web Vitals&quot;.
A geração de registros da extensão das Métricas da Web no console mostra dados de LoAF.

Usar dados de frames de animação longa em ferramentas de teste automatizado

Da mesma forma, as ferramentas de teste automatizado em pipelines de CI/CD podem mostrar detalhes sobre possíveis problemas de desempenho medindo frames de animação longos durante a execução de vários conjuntos de testes.

Perguntas frequentes

Algumas das perguntas frequentes sobre essa API incluem:

Por que não estender ou iterar na API Long Tasks?

Essa é uma abordagem alternativa para informar uma medição semelhante, mas diferente, de possíveis problemas de responsividade. É importante garantir que os sites que dependem da API Long Tasks continuem funcionando para evitar interrupções nos casos de uso.

Embora a API Long Tasks possa se beneficiar de alguns recursos da LoAF (como um modelo de atribuição melhor), acreditamos que o foco em frames, e não em tarefas, oferece muitos benefícios que tornam essa API fundamentalmente diferente da API Long Tasks.

Por que não tenho entradas de script?

Isso pode indicar que o frame de animação longo não foi causado pelo JavaScript, mas sim por um grande trabalho de renderização.

Isso também pode acontecer quando o frame de animação longo é devido ao JavaScript, mas a atribuição do script não pode ser fornecida por várias razões de privacidade, conforme observado anteriormente, principalmente porque o JavaScript não é de propriedade da página.

Por que tenho entradas de script, mas nenhuma ou poucas informações de origem?

Isso pode acontecer por vários motivos, incluindo não haver uma boa fonte para apontar.

As informações do script também serão limitadas apenas ao sourceURL (exceto redirecionamentos) para scripts no-cors cross-origin, com uma string vazia para sourceFunctionName e um -1 para sourceCharPosition. Isso pode ser resolvido buscando esses scripts usando o CORS, adicionando crossOrigin = "anonymous" à chamada <script>.

Por exemplo, o script padrão do Gerenciador de tags do Google a ser adicionado à página:

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->

Pode ser aprimorado para adicionar j.crossOrigin = "anonymous" e permitir que detalhes completos de atribuição sejam fornecidos ao GTM.

Isso vai substituir a API Long Tasks?

Acreditamos que a API Long Animation Frames seja melhor e mais completa para medir tarefas longas, mas, no momento, não há planos de descontinuar a API Long Tasks.

Feedback necessário

Você pode enviar feedback na lista de problemas do GitHub ou registrar bugs na implementação da API do Chrome no rastreador de problemas do Chrome.

Conclusão

A API Long Animation Frames é uma API nova e incrível com muitas vantagens em relação à API Long Tasks anterior.

Ela está se mostrando uma ferramenta fundamental para resolver problemas de capacidade de resposta, conforme medido pelo INP. A INP é uma métrica desafiadora de otimizar, e essa API é uma das maneiras que a equipe do Chrome está usando para facilitar a identificação e a solução de problemas para os desenvolvedores.

O escopo da API Long Animation Frames vai além do INP e pode ajudar a identificar outras causas de atualizações lentas que podem afetar a fluidez geral da experiência do usuário em um site.

Agradecimentos

Imagem em miniatura de Henry Be no Unsplash.