E além disso, a travada só é reproduzível em alguns sites. Pedi para o LLM ler um pouco do código do plugin; daqui a pouco vou chamar alguém mais experiente para ver se tem alguma ideia de otimização.
Do ponto de vista da arquitetura e da implementação do desenvolvimento de plugins, para os gargalos de desempenho causados pela tradução por LLM, é possível fazer uma otimização profunda a partir das seguintes dimensões centrais:
- Introduzir uma arquitetura de processamento assíncrono (Web Workers)
Atualmente, uma grande quantidade de correspondência por regex, parsing de HTML e comparação de textos traduzidos do plugin pode estar sendo executada no Content Script (thread principal).
- Solução de otimização: migrar para o Web Worker a lógica que não envolve operação direta no DOM (como parsing do protocolo de streaming do LLM, pré-processamento de texto, algoritmos de comparação multilíngue).
. - Efeito: liberar a thread principal, garantindo que, ao mesmo tempo em que o navegador processa os dados de tradução, ele ainda responda ao scroll e aos cliques do usuário a 60 fps.
- Implementar “renderização em lote” e “fatiamento de tempo” (Scheduling)
Em saída por streaming (Streaming), o mais problemático é receber um caractere e atualizar o DOM uma vez.
- Solução de otimização:
- Atualização com buffer (Buffering): definir um buffer de 100 ms–200 ms, mesclar vários fragmentos retornados pelo LLM e, em seguida, iniciar uma única atualização do DOM via
requestAnimationFrame. - Fatiamento de tempo (Time Slicing): se for preciso inserir muitos nós de tradução de uma vez, usar uma ideia semelhante ao React Fiber,
decompondo a tarefa em várias subtarefas pequenas e inserindo-as uma a uma quando o navegador estiver ocioso (requestIdleCallback).
- Atualização com buffer (Buffering): definir um buffer de 100 ms–200 ms, mesclar vários fragmentos retornados pelo LLM e, em seguida, iniciar uma única atualização do DOM via
- Efeito: transformar o reflow originalmente denso em atualizações ritmadas, reduzindo drasticamente a carga instantânea de CPU.
- “Renderização preguiçosa” baseada na viewport (Intersection Observer)
Se a página for muito longa, a tradução completa fará com que milhares de nós do DOM sejam atualizados simultaneamente em segundo plano, mesmo que esses nós não estejam na tela.
- Solução de otimização: usar a API IntersectionObserver.
- Apenas quando o nó do texto original entrar na viewport (ou estiver prestes a entrar) é que se dispara a solicitação de tradução e se monta o DOM de tradução.
- Para nós de tradução que saíram muito da viewport, pode-se considerar destruí-los ou ocultá-los temporariamente para reduzir a pressão de cálculo de layout do navegador.
- Efeito: reduzir a quantidade de computação de “escala da página inteira” para “escala da tela”.
- Otimizar a estratégia de montagem do DOM (Containment)
Ao inserir nós de tradução, se o layout original for quebrado, isso levará ao reflow de toda a página.
- Solução de otimização:
- CSS Containment: adicionar contain: layout; ou contain:
content; ao contêiner traduzido. Isso informa ao navegador que mudanças internas desse nó não afetarão o layout externo. - Placeholder fixo: antes de iniciar a tradução, estimar um espaço reservado de altura com base no comprimento do texto original, evitando que, durante o streaming do texto traduzido, a página “treme” repetidamente.
- CSS Containment: adicionar contain: layout; ou contain:
- Efeito: limitar o escopo do reflow, evitando que “uma mudança pequena afete o todo”.
- Reduzir disparos excessivos do MutationObserver
Como o próprio plugin modifica o DOM, isso acionará novamente seu próprio listener do MutationObserver.
- Solução de otimização:
- Ao inserir nós de tradução, bloquear temporariamente o listener por meio de um flag global (Flag) ou, no listener, filtrar os nós gerados pelo próprio plugin via node.isTrusted
ou atributos específicos. - Aumentar dinamicamente o limiar de mutationChangeDelay: ao detectar que o usuário está rolando rapidamente, aumentar automaticamente o atraso ou pausar o scan.
- Ao inserir nós de tradução, bloquear temporariamente o listener por meio de um flag global (Flag) ou, no listener, filtrar os nós gerados pelo próprio plugin via node.isTrusted
- “Atualização incremental” em vez de “substituição total” na saída por streaming
- Solução de otimização: se o nó de tradução for um contêiner, durante a atualização por streaming deve-se apenas anexar nós de texto, em vez de fazer innerHTML = …
reconstruindo a estrutura interna a cada vez. - Efeito: reduzir a pressão de coleta de lixo (GC) de memória e o overhead de parsing da árvore do DOM.
Resumo
O caminho de otimização mais eficaz é: Web Worker para processar dados →
requestAnimationFrame para renderização com buffer →
IntersectionObserver para limitar o escopo →
CSS contain para isolar o layout. Assim, é possível desacoplar perfeitamente a “carga pesada” do LLM da “fluidez” do navegador.
