Reporter le javascript et afficher le contenu provisoire lors du chargement initial de la page

Qu’en est-il de : Reporter les scripts javascript de Discourse

Ajoutez des attributs defer à tous les scripts javascript si possible. Reporter le chargement et l’exécution des scripts javascript permet au navigateur de commencer l’analyse HTML, le rendu et la peinture.

Ainsi, un contenu statique temporaire peut être affiché très tôt dans le processus de démarrage de Discourse (ou même avant). Cela devrait suffire pour une vitesse de chargement de page perçue par l’utilisateur plus rapide lors du premier chargement.

Idées de contenu statique temporaire :

  • écran de démarrage avec logo et indicateur de chargement
  • vue du sujet avec les publications du backend

POC et PR

Pour la dernière preuve de concept et le PR - veuillez consulter ce post.


Les scripts javascript du fournisseur et tous les scripts javascript précédents ne sont pas différés pour le moment.
@voir : https://github.com/rr-it/discourse/commit/328efd5c055f5f2a4d93b5e52268cfe92913faf7

Les idées sur la façon de résoudre ce problème sont les bienvenues.


Chargement javascript : async vs defer vs none

En savoir plus sur les options de chargement javascript - y compris defer : Efficiently load JavaScript with defer and async
(Il ne s’agit pas d’accélérer le véritable démarrage de Discourse.)


Fastboot/réhydratation

J’ai lu cet article :
La conclusion semble être une implémentation de Fastboot/réhydratation.
Y a-t-il un calendrier pour cela ?

4 « J'aime »

Cela provoquerait le LCP toujours après le démarrage et le ré-rendu d’EmberJS, sans résoudre le problème principal concernant les nouveaux classements Google.

C’est notre plan actuel à moyen terme pour résoudre le LCP dans Discourse.

2 « J'aime »

Depuis Chrome 88, ce n’est heureusement plus le cas ! :rocket:
Je ne le savais pas non plus jusqu’à maintenant. :))

« Avant ce changement, un élément supprimé n’était plus considéré comme un candidat LCP valide. […] Après ce changement, un élément supprimé est toujours considéré comme un candidat LCP valide. »

« Le changement consistant à inclure le contenu qui est ensuite supprimé du DOM comme potentiels plus grands éléments de contenu pour le rendu améliorera les temps de Largest Contentful Paint sur les sites qui ont des images [pour Discourse : des éléments de texte] de la même taille insérés plusieurs fois. C’est un schéma courant pour les carrousels, ainsi que pour certains frameworks JavaScript qui font du rendu côté serveur (server-side rendering). »


Journal des modifications LCP

Il pourrait y avoir d’autres bons changements à l’avenir :

1 « J'aime »

Voici des statistiques simulées pour une page de sujet avec le POC implémenté.

Lighthouse : « Les valeurs sont estimées et peuvent varier. »

WebPageTest

webpagetest.org

Simulation Moto4G


Note : nous sommes la flèche noire en haut.


Notes :

  • 0s-2s - écran vierge :
    WebPageTest ignore defer pour JavaScript et télécharge tous les scripts avant d’effectuer une première peinture - cela fonctionne correctement sur un appareil réel.
  • 2,5s - LCP : contenu statique du rendu côté serveur
  • 3,5s - Changement visuel : logo chargé
  • 6,5s - Changement visuel : contenu rendu par EmberJs
  • 7s - Visuel complet

PageSpeed Insights

Élément Largest Contentful Paint

PageSpeed identifie correctement le nœud de texte statique du rendu côté serveur comme élément FCP LCP :
div.row > div.topic-body > div.post > p

Nœud de texte rendu par EmberJs :
div.row > div.topic-body > div.regular.contents > div.cooked > p

Mais il semble que PageSpeed n’utilise pas le nœud de texte statique correctement identifié pour son résultat simulé : le FCP et le LCP simulés sont trop importants.

Données d’utilisateurs réels

Attendons encore 14 à 28 jours pour obtenir des données « réelles » du Chrome UX Report avec le POC implémenté.

Statistiques sans le POC implémenté pour la page de sujet testée :
(Les données concernent cette URL de sujet unique – et non l’ensemble de l’origine.)

4 « J'aime »

Oh, c’est une découverte très intéressante ! Excellent travail !

Qu’obtenez-vous avec cette extension https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma ?

3 « J'aime »

Via l’extension Chrome Web Vitals

  • sur ordinateur
  • Version Chromium 90.0.4430.212
  • premier chargement dans une nouvelle fenêtre de navigation privée


Remarque sur le délai de première entrée (First Input Delay) : J’ai attendu que la page soit complètement chargée, puis j’ai cliqué sur l’arrière-plan - donc après la fin du rendu EmberJs.


Remarque sur le délai de première entrée (First Input Delay) : Ici, j’ai cliqué sur l’arrière-plan immédiatement lorsque le contenu statique était visible pour la première fois. Ajoutez mon temps de réaction :sloth: en plus de ce FID.

Note supplémentaire sur les percentiles sous les barres de ces graphiques :
Les percentiles ne sont pas très pertinents car ils ne comparent que les valeurs mesurées aux valeurs d’origine. L’origine est une page web TYPO3 avec une installation de Discourse dans un sous-dossier.

2 « J'aime »

Super idée ! @rrit

Je suis tout à fait d’accord, Discourse est une application web très lente et gourmande en JS. Si nous pouvons différer les fichiers CSS/JS, cela aidera énormément à accélérer LCP, FCP, FID, CLS.

Ce serait vraiment utile de voir cela se concrétiser. Nous et beaucoup d’autres personnes rencontrons ce problème. Tous les sites Discourse échouent aux Core Web Vitals. Si nous servons une page HTML STATIQUE rapide aux utilisateurs lors du premier chargement et/ou si nous différons toute la logique JS/CSS lors du premier chargement initial, nous pourrons ainsi accélérer toutes les pages et passer les scores CWV ! J’ai hâte de voir cela en direct dans une mise à jour majeure de Discourse.

Tous les sites Discourse voient leur classement Google décliner car ils ne passent pas les Core Web Vitals.

2 « J'aime »

Nous sommes ouverts à l’expérimentation de cela dans le cœur. Le « flash » de contenu de styles différents peut être un peu déconcertant, nous aimerions donc commencer avec cela désactivé par défaut derrière un paramètre de site « expérimental ». De cette façon, les administrateurs du site peuvent choisir de l’activer s’ils le souhaitent.

Êtes-vous en mesure d’essayer d’ajouter un paramètre de site dans votre PR @rrit ? Il serait également bon d’ajouter des tests RSpec pour vérifier le comportement avec le paramètre activé / désactivé.

5 « J'aime »

Cela ne signifie-t-il pas que nous pouvons simplement placer un spinner plein écran (qui a 100% de largeur et 100% de hauteur) sur la page rendue par le serveur, et le remplacer par l’application Ember lorsqu’elle démarre enfin pour obtenir un LCP extrêmement bas ?

Nous pourrions faire de ce spinner un SVG qui imite l’interface utilisateur de Discourse afin que la transition soit plus fluide et moins de type FOUC.

2 « J'aime »

Je pense que la partie clé est LCP « candidat »

Il ne sera considéré comme la plus grande peinture de contenu que s’il est réellement le plus grand (ou au moins de la même taille) que ce qui est finalement rendu ?

Donc, l’utilisation de la vue crawler fonctionne assez bien car le contenu (c’est-à-dire le texte) est en grande partie le même ?

(Je spécule surtout ici, d’après les captures d’écran ci-dessus - je n’ai pas essayé de spinner plein écran)

1 « J'aime »

Le Feature flag est implémenté.

Je ne suis pas du tout un développeur Ruby - là-dessus, j’ai vraiment besoin d’aide.

Peut-être pousser mon POC dans une nouvelle branche du dépôt discourse/discourse, avant de faire un PR sur main ?

Voici mon PR sur cette fonctionnalité :

@david Pouvez-vous me prêter votre tête pour obtenir de l’aide sur le développement de tests Rspec pour ces changements :

app/helpers/application_helper.rb : spec/helpers/application_helper_spec.rb

Je ne vois pas de tests unitaires réalisables ici. Cela semble testable uniquement par des tests d’intégration.
app/models/theme.rb
app/models/theme_field.rb

J’ai dû désactiver le defer tag pour le QUnit Test Runner : app/views/qunit/index.html.erb
Avant, les tests QUnit s’exécutaient toujours avec le Feature flag "javascript defer" = false. Et maintenant, les tests s’exécutent aussi avec "javascript defer" = true.

2 « J'aime »

Ceci est probablement déjà bloqué par Chrome Speed - Largest Contentful Paint Bug Fixes in M88 :

Les images plein écran, qui sont visuellement équivalentes aux images d’arrière-plan, ne sont plus considérées comme le plus grand élément d’affichage de contenu (largest contentful paint).


Bon point : voir Largest Contentful Paint (LCP)  |  Articles  |  web.dev

Pour les éléments de texte, seule la taille de leurs nœuds de texte est prise en compte (le plus petit rectangle qui englobe tous les nœuds de texte).

Pour tous les éléments, les marges, le rembourrage ou les bordures appliqués via CSS ne sont pas pris en compte.

  • C’est pourquoi le nœud de texte statique doit être rendu exactement de la même taille que le nœud de texte EmberJs.
  • Ou même légèrement plus grand en augmentant la line-height.
    Par exemple, si la largeur des nœuds de texte ne correspond pas, il existe de nombreux cas géométriques introduits par différents sauts de ligne où le nœud de texte statique devient plus petit que celui d’EmberJs.

Voir : Exemples LCP


J’ai en fait utilisé le rendu noscript des publications à l’intérieur d’une page de sujet. Les classes CSS correspondent légèrement aux vraies - donc l’apparence est égale.

Voir : Changements apportés à app/views/layouts/application.html.erb

Edit : Ma faute, c’est en fait la vue crawler : app/views/topics/show.html.erb

2 « J'aime »

Dans le POC, deux fonctionnalités sont combinées - devons-nous les diviser en deux drapeaux de fonctionnalités expérimentales ?

  • JavaScript avec balise defer (drapeau de fonctionnalité dans le tableau de bord des paramètres)
    (drapeau de fonctionnalité caché car une reconstruction du conteneur ou une purge du cache du thème est nécessaire pour cela)Correction : commutation à chaud avec cache
  • Affichage de contenu statique dans la vue du sujet (drapeau de fonctionnalité dans le tableau de bord des paramètres)

Voici les drapeaux de fonctionnalités : feature flags


Bien sûr, l’impact complet sur le LCP n’est obtenu qu’en utilisant les deux : FCP : contenu statique

Il pourrait y avoir des instances de Discourse où les plugins ou les composants de thème échouent lors de la mise en attente du JS. En divisant ces fonctionnalités, elles peuvent obtenir un petit gain sur le contenu statique sans mettre en attente le JS : FCP : contenu statique sans mise en attente du JS

2 « J'aime »

Première impression de Google Search Console avec le POC appliqué depuis le 2022-01-30 :

Ordinateur

Il a fallu du temps pour que les résultats apparaissent sur ordinateur :


Remarque : la vieille ligne de base verte représente les pages web non-Discourse sur le même domaine.

Mobile


Remarque : la vieille ligne de base verte représente les pages web non-Discourse sur le même domaine.

Attendons encore 7 à 14 jours pour voir, espérons-le, davantage d’améliorations pour les pages mobiles, car les valeurs sont moyennées sur les 28 derniers jours - seulement 12 jours comptent avec le POC appliqué actuellement.

5 « J'aime »

Résumé du PoC sur le LCP

Le PoC est appliqué depuis le 30/01/2022 et il a fallu plus de 4 semaines pour affecter toutes les pages dans le rapport « Core Web Vitals » de la Google Search Console - basé sur les données CrUX.

Toutes les pages de sujets sont dans la zone verte LCP (mesurée par CrUX) :

  • Bureau : LCP 1,7 s
  • Mobile : LCP 2,0 s

Données LCP : Google Search Console/CrUX

Impression de la Google Search Console avec le PoC appliqué depuis le 30/01/2022 :

Bureau


Note : la vieille ligne de base verte représente les pages web non-Discourse sur le même domaine.

Bonnes URLs

Mobile


Note : la vieille ligne de base verte représente les pages web non-Discourse sur le même domaine.

Bonnes URLs

Problème LCP : plus de 2,5 s (mobile)


Note : seules les pages de sujets affichent du contenu statique avant le contenu EmberJS


Approbation du PR avec des feature flags nécessaire

@sam Pourriez-vous déléguer ce PR à quelqu’un pour qu’il l’examine et l’approuve, s’il vous plaît.

3 « J'aime »

Nous l’examinerons certainement attentivement, c’est un changement très important qui pourrait nous prendre un peu de temps.

5 « J'aime »

@rrit merci de partager les données de votre site ! Nous en avons discuté en interne et je crains que nous n’ajoutions pas cette fonctionnalité au cœur de Discourse pour le moment.

Bien que les métriques Web Vital que vous avez partagées soient très impressionnantes, le flash de contenu de « vue crawler » n’offre pas une bonne expérience utilisateur. Les modifications de style que vous avez apportées aident certainement, mais elles devront être ajustées pour chaque site Discourse ayant un style personnalisé.

Notre objectif à long terme est de mettre en œuvre un véritable rendu côté serveur à l’aide de quelque chose comme Ember FastBoot. Théoriquement, cela fournirait les mêmes améliorations statistiques que celles que vous avez mesurées, tout en offrant une expérience utilisateur transparente. Nous préférerions concentrer nos efforts sur cet objectif.


Cela dit, Discourse est super extensible, donc je pense qu’il devrait être tout à fait possible de mettre en œuvre votre idée dans un plugin Discourse et de le partager ici dans Plugin.

Le plus grand changement que vous avez apporté dans le PR principal est d’ajouter l’attribut defer aux balises script. Remplacer tous ces emplacements à partir d’un plugin serait très difficile. Cependant, je pense que le même résultat pourrait être obtenu avec une approche basée sur des middlewares. J’ai trouvé cet article de blog qui décrit un problème similaire :

En utilisant cette technique, vous pourriez écrire un middleware qui vérifie les réponses text/html, les analyse, puis ajoute des attributs defer si nécessaire.

L’ajout de middlewares à partir d’un plugin peut se faire comme ceci :

# name: my-plugin
# about: Ma description de plugin
# version: 1.0
# url: https://example.org

require_relative "lib/script_defer_middleware"

on(:after_initializers) do
  Rails.configuration.middleware.use(ScriptDeferMiddleware)
end

Si vous rencontrez des obstacles avec une approche basée sur des plugins, n’hésitez pas à poster ici et nous serons heureux d’essayer de vous orienter dans la bonne direction.

10 « J'aime »

Si je trouve le temps pour cela, j’implémenterai probablement un plugin.

Mais pour l’instant, j’essaie de m’en sortir avec une approche de patch dans web_only.yml :

# pseudo-code non testé !
hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          - curl https://patch-diff.githubusercontent.com/raw/discourse/discourse/pull/15858.diff | git apply
4 « J'aime »

Ember FastBoot semble être une approche à long terme parfaite. Pendant ce temps, le sujet LCP reste brûlant :

2 « J'aime »

Merci d’avoir travaillé sur ce sujet @rrit :+1:

J’ai une bonne nouvelle, nous avons implémenté une nouvelle fonctionnalité dans Discourse, qui devrait beaucoup aider à résoudre ce problème.

5 « J'aime »