Aplazar javascript y mostrar contenido provisional en la carga inicial de la página

¿Qué tal: Posponer los javascripts de Discourse

Añade atributos defer a todos los javascripts si es posible. Posponer la carga y ejecución de javascript permite al navegador comenzar el análisis de HTML, la representación y el pintado.

Así, cierto contenido estático provisional puede mostrarse muy pronto (o incluso antes) del proceso de arranque de Discourse. Esto debería ser suficiente para una velocidad de carga de página percibida por el usuario más rápida en la carga inicial de la página.

Ideas para contenido estático provisional:

  • pantalla de bienvenida con logo y spinner de carga
  • vista de tema con publicaciones del backend

POC y PR

Para la última prueba de concepto y PR, por favor, mira esta publicación.


Actualmente, el javascript del proveedor y todos los javascripts anteriores no se posponen.
@ver: https://github.com/rr-it/discourse/commit/328efd5c055f5f2a4d93b5e52268cfe92913faf7

Se agradecen mucho las ideas sobre cómo resolver esto.


JavaScript async vs. defer vs. none

Más sobre las opciones de carga de javascript, incluido defer: Efficiently load JavaScript with defer and async
(Esto no trata de acelerar el arranque real de Discourse).


Fastboot/rehidratación

Leí este artículo:
La conclusión allí parece ser una implementación de Fastboot/rehidratación.
¿Hay un cronograma para esto?

4 Me gusta

Eso haría que el LCP siguiera activo después de que EmberJS arranque y se vuelva a renderizar, sin abordar el problema principal con respecto a las nuevas clasificaciones de Google.

Ese es nuestro plan actual a medio plazo para abordar el LCP en Discourse.

2 Me gusta

¡A partir de Chrome 88, esto, por suerte, ya no es cierto! :rocket:
Tampoco lo sabía hasta ahora. :))

“Antes de este cambio, la eliminación de un elemento hacía que dejara de considerarse un candidato válido para LCP. […] Después de este cambio, la eliminación de un elemento todavía se considera un candidato válido para LCP”.

“El cambio para incluir contenido que posteriormente se elimina del DOM como posibles pintados de contenido más grandes mejorará los tiempos de Largest Contentful Paint en sitios que tienen imágenes [para Discourse: elementos de texto] del mismo tamaño insertados varias veces. Este es un patrón común para los carruseles, así como para algunos frameworks de JavaScript que hacen renderizado del lado del servidor”.


Registro de cambios de LCP

Puede haber más cambios buenos en el futuro:

1 me gusta

Aquí hay algunas estadísticas simuladas para una página de tema con el POC implementado.

Lighthouse: “Los valores son estimados y pueden variar.”

WebPageTest

webpagetest.org

Simulación Moto4G


Nota: somos la flecha negra en la parte superior.


Notas:

  • 0s-2s - pantalla en blanco:
    WebPageTest ignora defer para JavaScript y descarga todos los JavaScript antes de realizar una primera pintura; esto funciona correctamente en un dispositivo real.
  • 2.5s - LCP: contenido estático de la renderización del lado del servidor
  • 3.5s - Cambio visual: logotipo cargado
  • 6.5s - Cambio visual: contenido de la renderización de EmberJs
  • 7s - Visual completo

PageSpeed Insights

Elemento Largest Contentful Paint

PageSpeed identifica correctamente el nodo de texto estático de la renderización del lado del servidor como el elemento FCP LCP:
div.row > div.topic-body > div.post > p

Nodo de texto renderizado por EmberJs:
div.row > div.topic-body > div.regular.contents > div.cooked > p

Pero parece que PageSpeed no utiliza el nodo de texto estático correctamente identificado para su resultado simulado: el FCP y LCP simulados son demasiado grandes.

Datos de usuarios del mundo real

Esperemos otros 14-28 días para obtener datos “reales” del Chrome UX Report con el POC implementado.

Estadísticas sin el POC implementado para la página de tema probada:
(Los datos son de esta única URL de tema, y no de todo el origen).

4 Me gusta

¡Oh, ese es un descubrimiento muy interesante! ¡Gran trabajo!

¿Qué obtienes en esta extensión https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma?

3 Me gusta

A través de la extensión de Chrome Web Vitals

  • en escritorio
  • Versión de Chromium 90.0.4430.212
  • primera carga en una nueva ventana de incógnito


Nota sobre el First Input Delay (Retraso de la primera entrada): Esperé hasta que la página se cargó completamente y luego hice clic en el fondo, es decir, después de que el renderizado de EmberJs terminara.


Nota sobre el First Input Delay (Retraso de la primera entrada): Aquí hice clic en el fondo inmediatamente cuando el contenido estático fue visible por primera vez. Añade mi tiempo de reacción :sloth: encima de este FID.

Nota adicional sobre los percentiles debajo de las barras en estos gráficos:
Los percentiles no son muy relevantes ya que solo comparan los valores medidos con los valores de origen. El origen es una página web de TYPO3 con una instalación de Discourse en una subcarpeta.

2 Me gusta

¡Gran idea! @rrit

Estoy totalmente de acuerdo en que Discourse es una aplicación web muy lenta y pesada en JS. Si podemos diferir los archivos CSS/JS, ayudará enormemente a acelerar LCP, FCP, FID, CLS.

Realmente ayudaría ver esto implementado, nosotros y muchas otras personas estamos enfrentando este problema. Todos los sitios de Discourse fallan en Core Web Vitals. Si servimos una página HTML estática rápida a los usuarios la primera vez y/o diferimos toda la lógica de JS/CSS en la primera carga inicial, ¡podemos acelerar todas las páginas y pasar las puntuaciones de CWV! Emocionado de ver esto implementado en una actualización principal de Discourse.

Todos los sitios de Discourse están perdiendo posiciones en Google debido a que los sitios no pasan Core Web Vitals.

2 Me gusta

Estamos abiertos a experimentar con esto en el núcleo. El “flash” de contenido con un estilo diferente puede ser un poco desconcertante, por lo que nos gustaría empezar con él detrás de una configuración del sitio “experimental” deshabilitada por defecto para empezar. De esa manera, los administradores del sitio pueden optar por habilitarlo si lo desean.

¿Puedes intentar agregar una configuración del sitio en tu PR @rrit? También sería bueno agregar algunas pruebas RSpec para verificar el comportamiento con la configuración habilitada / deshabilitada.

5 Me gusta

¿No significa eso que podemos simplemente poner un spinner a pantalla completa (que tiene 100% de ancho y 100% de alto) en la página renderizada por el servidor, y reemplazarlo por la aplicación Ember cuando finalmente arranca para obtener un LCP extremadamente bajo?

Podríamos hacer que este spinner sea un SVG que imite la interfaz de usuario de Discourse para que la transición sea más fluida y menos parecida a FOUC.

2 Me gusta

Creo que la parte clave es LCP “candidato

¿Solo se considerará la pintura de contenido más grande si realmente es la más grande (o al menos, del mismo tamaño) que lo que finalmente se renderiza?

¿Entonces usar la vista del rastreador funciona bastante bien porque el contenido (es decir, el texto) es en gran medida el mismo?

(Estoy adivinando aquí, basándome en las capturas de pantalla anteriores; no he probado un spinner a pantalla completa)

1 me gusta

La bandera de funcionalidad está implementada.

No soy en absoluto un desarrollador de Ruby; en esto, definitivamente necesito ayuda.

¿Quizás subir mi POC a una nueva rama en el repositorio discourse/discourse, antes de hacer un PR en main?

Este es mi PR sobre esta funcionalidad:

@david ¿Puedes prestarme tu cabeza para obtener ayuda en el desarrollo de pruebas Rspec para estos cambios:

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

No veo pruebas unitarias factibles aquí. Parece que solo se pueden probar mediante pruebas de integración.
app/models/theme.rb
app/models/theme_field.rb

Tuve que deshabilitar el defer tag para QUnit Test Runner: app/views/qunit/index.html.erb
Antes, las pruebas QUnit todavía se ejecutaban con la bandera de funcionalidad "javascript defer" = false. Y ahora las pruebas también se ejecutan con "javascript defer" = true.

2 Me gusta

Esto probablemente ya está bloqueado por https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/2020_11_lcp.md:

Las imágenes de pantalla completa, que son visualmente equivalentes a las imágenes de fondo, ya no se consideran el mayor elemento de pintura con contenido (largest contentful paint).


Buen punto: véase Largest Contentful Paint (LCP)  |  Articles  |  web.dev

Para los elementos de texto, solo se considera el tamaño de sus nodos de texto (el rectángulo más pequeño que abarca todos los nodos de texto).

Para todos los elementos, no se consideran los márgenes, rellenos o bordes aplicados a través de CSS.

  • Es por eso que el nodo de texto estático debe renderizarse exactamente del mismo tamaño que el nodo de texto de EmberJs.
  • O incluso ligeramente más grande aumentando el line-height.
    Por ejemplo, si el ancho de los nodos de texto no coincide, hay muchos casos geométricos introducidos por diferentes saltos de línea en los que el nodo de texto estático se vuelve más pequeño que el de EmberJs.

Véase: Ejemplos de LCP


En realidad, utilicé la representación noscript de las publicaciones dentro de la página de un tema. Las clases CSS coinciden ligeramente con las reales, por lo que el aspecto es el mismo.

Véase: Cambios en app/views/layouts/application.html.erb

Editar: Mi error, esta es en realidad la vista del rastreador: app/views/topics/show.html.erb

2 Me gusta

En el POC hay dos características combinadas, ¿las separamos en dos indicadores de características experimentales?

  • JavaScript con etiqueta defer (indicador de características en el panel de configuración)
    (indicador de características oculto ya que se necesita una reconstrucción del contenedor o un vaciado de la caché del tema)Arreglo: cambio en caliente con caché
  • Mostrar contenido estático en la vista del tema (indicador de características en el panel de configuración)

Aquí vamos: indicadores de características


Por supuesto, el impacto total en LCP solo se logra utilizando ambos: FCP: contenido estático

Puede haber instancias de Discourse donde los plugins o componentes temáticos fallen al diferir el JS. Al separar estas características, pueden obtener una pequeña ganancia en el contenido estático sin diferir el JS: FCP: contenido estático sin diferir JS

2 Me gusta

Primera impresión de Google Search Console con el POC aplicado desde el 30/01/2022:

Escritorio

Los resultados de escritorio tardaron un tiempo en aparecer:


Nota: la línea base verde antigua representa páginas web no de Discourse en el mismo dominio.

Móvil


Nota: la línea base verde antigua representa páginas web no de Discourse en el mismo dominio.

Esperemos otros 7-14 días para ver si hay más mejoras en las páginas móviles, ya que los valores se promedian durante los últimos 28 días, y actualmente solo han transcurrido 12 días con el POC aplicado.

5 Me gusta

Resumen de LCP sobre Prueba de Concepto

La Prueba de Concepto (POC) se aplica desde el 30/01/2022 y tardó más de 4 semanas en afectar a todas las páginas en el informe “Core Web Vitals” de Google Search Console, basado en datos de CrUX.

Todas las páginas de temas están en la zona verde de LCP (medido por CrUX):

  • Escritorio: LCP 1.7 seg
  • Móvil: LCP 2.0 seg

Datos de LCP: Google Search Console/CrUX

Impresión de Google Search Console con la POC aplicada desde el 30/01/2022:

Escritorio


Nota: la línea base verde antigua representa páginas web no-Discourse en el mismo dominio.

URLs Buenas

Móvil


Nota: la línea base verde antigua representa páginas web no-Discourse en el mismo dominio.

URLs Buenas

Problema de LCP: más de 2.5s (móvil)


Nota: Solo las páginas de temas muestran contenido estático antes del contenido EmberJS


Se necesita la aprobación de PR con flags de funcionalidad

@sam ¿Puedes delegar este PR a alguien para que lo revise y apruebe, por favor?

3 Me gusta

Ciertamente la revisaremos cuidadosamente, es un cambio muy grande y puede que tardemos un poco en llegar a él.

5 Me gusta

@rrit ¡Gracias por compartir los datos de tu sitio! Hemos estado discutiendo esto internamente y me temo que no agregaremos esta funcionalidad al núcleo de Discourse por el momento.

Si bien las métricas de Web Vital que compartiste son muy impresionantes, el destello de contenido de ‘vista de rastreador’ no crea una buena experiencia de usuario. Los cambios de estilo que has realizado ciertamente ayudan, pero deberán ajustarse para cada sitio de Discourse que tenga estilo personalizado.

Nuestro objetivo a largo plazo es implementar la renderización real del lado del servidor utilizando algo como Ember FastBoot. Teóricamente, eso proporcionaría las mismas mejoras estadísticas que has medido, al tiempo que ofrecería una experiencia de usuario fluida. Preferiríamos centrar nuestros esfuerzos en ese objetivo.


Dicho todo esto, Discourse es súper extensible, así que creo que debería ser totalmente posible implementar tu idea en un plugin de Discourse y luego compartirlo aquí en Plugin.

El mayor cambio que has hecho en el PR principal es agregar el atributo defer a las etiquetas de script. Anular todos esos lugares desde un plugin sería muy difícil. Sin embargo, creo que el mismo resultado podría lograrse con un enfoque basado en middleware. Encontré esta publicación de blog que describe un problema similar:

Usando esa técnica, podrías escribir un middleware que verifique las respuestas text/html, las analice y luego agregue atributos defer donde sea necesario.

Agregar middleware desde un plugin se puede hacer algo como esto:

# name: my-plugin
# about: Mi descripción del 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 encuentras algún obstáculo con un enfoque basado en plugins, no dudes en publicar aquí y estaremos encantados de intentar indicarte la dirección correcta.

10 Me gusta

Si encuentro tiempo para esto, probablemente implementaré un plugin.

Pero por ahora, intento arreglármelas con un enfoque de parcheo en web_only.yml:

# ¡pseudocódigo no probado!
hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          - curl https://patch-diff.githubusercontent.com/raw/discourse/discourse/pull/15858.diff | git apply
4 Me gusta

Ember FastBoot parece un enfoque perfecto a largo plazo. Mientras tanto, el tema de LCP sigue candente:

2 Me gusta

Gracias por trabajar en esto @rrit :+1:

Tengo buenas noticias, hemos implementado una nueva función en Discourse que debería ayudar bastante con esto.

5 Me gusta