De plus, les saccades ne sont reproductibles que sur certains sites. J’ai demandé à un LLM de lire le code du plugin ; tout à l’heure, je vais demander à un expert de jeter un œil pour voir s’il y a des pistes d’optimisation.
Du point de vue de l’architecture et de l’implémentation du développement de plugins, pour les goulots d’étranglement de performance causés par la traduction via LLM, on peut optimiser en profondeur selon les dimensions clés suivantes :
- Introduire une architecture de traitement asynchrone (Web Workers)
Actuellement, une grande quantité de correspondances par expressions régulières, d’analyse HTML et de comparaison de texte traduit du plugin sont probablement exécutées dans le Content Script (thread principal).
- Solution d’optimisation : migrer vers un Web Worker la logique qui n’implique pas d’opérations directes sur le DOM (comme l’analyse du protocole de streaming du LLM, le prétraitement du texte, les algorithmes de comparaison multilingue).
- Effet : libérer le thread principal, afin que le navigateur, tout en traitant les données de traduction, puisse toujours répondre à 60 fps aux actions de défilement et de clic de l’utilisateur.
- Mettre en œuvre le « rendu par lots » et le « découpage temporel » (Scheduling)
Avec une sortie en streaming (Streaming), il faut absolument éviter de mettre à jour le DOM à chaque caractère reçu.
- Solution d’optimisation :
- Mise à jour avec tampon (Buffering) : définir un tampon de 100 ms à 200 ms, fusionner plusieurs fragments renvoyés par le LLM, puis déclencher une mise à jour DOM via requestAnimationFrame.
- Découpage temporel (Time Slicing) : s’il faut insérer d’un coup un grand nombre de nœuds de traduction, utiliser une idée similaire à React Fiber, découper la tâche en plusieurs petites tâches, et insérer progressivement lorsque le navigateur est inactif (requestIdleCallback).
- Effet : transformer des reflows (Reflow) initialement denses en mises à jour cadencées, réduisant fortement la charge CPU instantanée.
- « Rendu paresseux » basé sur le viewport (Intersection Observer)
Si la page est très longue, une traduction intégrale entraînera la mise à jour en arrière-plan de milliers de nœuds DOM simultanément, même si ces nœuds ne sont pas à l’écran.
- Solution d’optimisation : utiliser l’API IntersectionObserver.
- Ne déclencher la requête de traduction et ne monter le DOM de traduction que lorsque le nœud source entre dans le viewport (ou est sur le point d’y entrer).
- Pour les nœuds de traduction éloignés du viewport, on peut envisager de les détruire temporairement ou de les masquer afin d’alléger la pression du calcul de mise en page du navigateur.
- Effet : réduire la charge de calcul de « l’échelle de la page entière » à « l’échelle de l’écran ».
- Optimiser la stratégie de montage DOM (Containment)
Lors de l’insertion de nœuds de traduction, si la mise en page d’origine est perturbée, cela peut provoquer un reflow de toute la page.
- Solution d’optimisation :
- CSS Containment : ajouter contain: layout; ou contain:
content; au conteneur traduit. Cela indique au navigateur que les changements internes de ce nœud n’affecteront pas la mise en page externe. - Espace réservé fixe : avant de démarrer la traduction, estimer une hauteur d’espace réservé en fonction de la longueur du texte source, afin d’éviter que le texte traduit ne « saute » pendant le streaming et ne provoque des « oscillations » répétées de la page.
- CSS Containment : ajouter contain: layout; ou contain:
- Effet : limiter le périmètre des reflows et éviter un effet « tirer un cheveu et tout bouge ».
- Réduire les déclenchements excessifs de MutationObserver
Comme le plugin modifie lui-même le DOM, cela redéclenche son propre MutationObserver.
- Solution d’optimisation :
- Lors de l’insertion de nœuds de traduction par le plugin, désactiver temporairement l’écoute via un indicateur global (Flag), ou filtrer dans l’écouteur les nœuds générés par le plugin lui-même via node.isTrusted
ou via des attributs spécifiques. - Augmenter dynamiquement le seuil de mutationChangeDelay : lorsqu’un défilement rapide de l’utilisateur est détecté, augmenter automatiquement le délai ou suspendre l’analyse.
- Lors de l’insertion de nœuds de traduction par le plugin, désactiver temporairement l’écoute via un indicateur global (Flag), ou filtrer dans l’écouteur les nœuds générés par le plugin lui-même via node.isTrusted
- Pour la sortie en streaming, faire des « mises à jour incrémentales » plutôt que des « remplacements complets »
- Solution d’optimisation : si le nœud de traduction est un conteneur, lors des mises à jour en streaming il faut seulement ajouter des nœuds texte, au lieu de faire innerHTML = …
et de reconstruire à chaque fois la structure interne. - Effet : réduire la pression du ramasse-miettes (GC) et le coût d’analyse de l’arbre DOM.
Résumé
Le chemin d’optimisation le plus efficace est : Web Worker pour traiter les données →
requestAnimationFrame pour tamponner le rendu →
IntersectionObserver pour limiter le périmètre →
CSS contain pour isoler la mise en page. Ainsi, on peut découpler parfaitement la « charge lourde » du LLM et la « fluidité » du navigateur.
