Adiar javascript e mostrar conteúdo provisório no carregamento inicial da página

Que tal: Adiar os scripts do Discourse

Adicione atributos defer a todos os scripts, se possível. Adiar o carregamento e a execução de JavaScript permite que o navegador comece a analisar o HTML, renderizar e pintar.

Assim, algum conteúdo estático intermediário pode ser exibido bem no início (ou até mesmo antes) do processo de inicialização do Discourse. Isso deve ser suficiente para uma velocidade de carregamento de página percebida pelo usuário mais rápida na primeira carga da página.

Ideias para conteúdo estático intermediário:

  • tela de splash com logotipo e spinner de carregamento
  • visualização de tópico com postagens do backend

POC e PR

Para a prova de conceito e PR mais recentes - por favor, veja esta postagem.


O JavaScript do fornecedor e todos os scripts anteriores não estão sendo adiados no momento.
@ver: https://github.com/rr-it/discourse/commit/328efd5c055f5f2a4d93b5e52268cfe92913faf7

Ideias sobre como resolver isso são muito bem-vindas.


JavaScript async vs. defer vs. none

Mais sobre opções de carregamento de JavaScript - incluindo defer: Efficiently load JavaScript with defer and async
(Isso não é sobre acelerar a inicialização real do Discourse.)


Fastboot/rehydration

Li este artigo:
A conclusão lá parece ser uma implementação de Fastboot/rehydration.
Existe um cronograma para isso?

4 curtidas

Isso faria com que o LCP ainda ocorresse após o EmberJS inicializar e renderizar novamente, não abordando o problema principal em relação às novas classificações do Google.

Esse é o nosso plano atual de médio prazo para abordar o LCP no Discourse.

2 curtidas

A partir do Chrome 88, isso felizmente não é mais verdade! :rocket:
Também não sabia disso até agora. :))

“Antes desta alteração, um elemento que era removido fazia com que ele não fosse mais considerado um candidato válido para LCP. […] Após esta alteração, um elemento que é removido ainda é considerado um candidato válido para LCP.”

“A alteração para incluir conteúdo que é posteriormente removido do DOM como possíveis maiores pinturas de conteúdo melhorará os tempos de Largest Contentful Paint em sites que possuem imagens [para Discourse: elementos de texto] do mesmo tamanho inseridos várias vezes. Este é um padrão comum para carrosséis, bem como para alguns frameworks JavaScript que fazem renderização do lado do servidor.”


LCP Changelog

Pode haver mais boas mudanças no futuro:

1 curtida

Aqui estão algumas estatísticas simuladas para uma página de tópico com o POC implementado.

Lighthouse: “Os valores são estimados e podem variar.”

WebPageTest

webpagetest.org

Simulação Moto4G


Nota: nós somos a seta preta no topo.


Notas:

  • 0s-2s - tela em branco:
    O WebPageTest ignora defer para JavaScript e baixa todos os JavaScripts antes de fazer uma primeira pintura - isso funciona corretamente em um dispositivo real.
  • 2.5s - LCP: conteúdo estático do renderização do lado do servidor
  • 3.5s - Mudança Visual: logo carregado
  • 6.5s - Mudança Visual: conteúdo do renderização do EmberJs
  • 7s - Completo Visual

PageSpeed Insights

Elemento Largest Contentful Paint

O PageSpeed identifica corretamente o nó de texto estático da renderização do lado do servidor como o elemento FCP LCP:
div.row > div.topic-body > div.post > p

Nó de texto renderizado pelo EmberJs:
div.row > div.topic-body > div.regular.contents > div.cooked > p

Mas parece que o PageSpeed não usa o nó de texto estático corretamente identificado para seu resultado simulado: FCP e LCP simulados são muito grandes.

Dados de usuários do mundo real

Vamos esperar mais 14-28 dias para obter dados “reais” do Chrome UX Report com o POC implementado.

Estatísticas sem o POC implementado para a página de tópico testada:
(Os dados são desta única URL de tópico – e não de toda a origem.)

4 curtidas

Oh, essa é uma descoberta muito interessante! Ótimo trabalho!

O que você obtém nesta extensão https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma?

3 curtidas

Via Extensão Chrome Web Vitals

  • no desktop
  • Versão do Chromium 90.0.4430.212
  • primeira carga em uma nova janela anônima


Observação sobre o First Input Delay: Esperei até a página carregar completamente e então cliquei no fundo - então, depois que a renderização do EmberJs terminou.


Observação sobre o First Input Delay: Aqui cliquei no fundo imediatamente quando o conteúdo estático ficou visível pela primeira vez. Adicione meu tempo de reação :sloth: em cima deste FID.

Observação extra sobre percentis abaixo das barras nestes gráficos:
Os percentis não são tão relevantes, pois apenas comparam os valores medidos com os valores de origem. A origem é uma página web TYPO3 com uma instalação de Discourse em uma subpasta.

2 curtidas

Ideia incrível! @rrit

Concordo totalmente que o Discourse é um aplicativo web muito lento e pesado em JS. Se pudermos adiar os ARQUIVOS CSS/JS, isso ajudará enormemente a acelerar LCP, FCP, FID, CLS.

Seria muito útil ver isso sendo implementado. Nós e muitas outras pessoas estamos enfrentando esse problema. Todos os sites do Discourse estão falhando no Core Web Vitals. Se servirmos uma página HTML ESTÁTICA rápida aos usuários na primeira vez e/ou adiarmos toda a lógica JS/CSS na primeira carga inicial, dessa forma poderemos acelerar todas as páginas e passar nas pontuações do CWV! Animado para ver isso implementado em uma atualização principal do Discourse.

Todas as classificações do Google dos sites do Discourse estão caindo devido aos sites não passarem no Core Web Vitals.

2 curtidas

Estamos abertos a experimentar isso no core. O ‘flash’ de conteúdo com estilos diferentes pode ser um pouco desconcertante, então gostaríamos de começar com ele desativado por padrão em uma configuração de site “experimental”. Dessa forma, os administradores do site podem optar por ativá-lo, se quiserem.

Você consegue tentar adicionar uma configuração de site em seu PR @rrit? Também seria bom adicionar alguns testes RSpec para verificar o comportamento com a configuração ativada / desativada.

5 curtidas

Isso não significa que podemos simplesmente colocar um spinner em tela cheia (que tem 100% de largura e 100% de altura) na página renderizada pelo servidor e substituí-lo pelo aplicativo Ember quando ele finalmente inicializar para obter um LCP extremamente baixo?

Poderíamos fazer esse spinner ser um SVG que imita a interface do usuário do Discourse para que a transição seja mais suave e menos parecida com FOUC.

2 curtidas

Acho que a parte chave é LCP “candidato

Ele só será considerado a maior pintura de conteúdo se realmente for o maior (ou pelo menos, do mesmo tamanho) do que é eventualmente renderizado?

Então, usar a visualização do rastreador funciona muito bem porque o conteúdo (ou seja, o texto) é em grande parte o mesmo?

(Estou principalmente adivinhando aqui, com base nas capturas de tela acima - eu não tentei um spinner em tela cheia)

1 curtida

O feature flag foi implementado.

Eu não sou um desenvolvedor Ruby - nisso eu definitivamente preciso de ajuda.

Talvez enviar meu POC para um novo branch no repositório discourse/discourse, antes de fazer um PR no main?

Este é o meu PR sobre este recurso:

@david Você pode me dar uma mãozinha para desenvolver testes Rspec para essas alterações:

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

Eu não vejo testes unitários viáveis aqui. Parece testável apenas por testes de integração.
app/models/theme.rb
app/models/theme_field.rb

Eu tive que desabilitar o defer tag para o QUnit Test Runner: app/views/qunit/index.html.erb
Antes, os testes QUnit ainda rodavam com o feature flag "javascript defer" = false. E agora os testes rodam também com "javascript defer" = true.

2 curtidas

Isso provavelmente já é bloqueado por https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/2020_11_lcp.md:

Imagens de viewport completo, que são visualmente equivalentes a imagens de fundo, não são mais consideradas como a maior pintura de conteúdo (largest contentful paint)


Bom ponto: veja Largest Contentful Paint (LCP)  |  Articles  |  web.dev

Para elementos de texto, apenas o tamanho de seus nós de texto é considerado (o menor retângulo que abrange todos os nós de texto).

Para todos os elementos, qualquer margem, preenchimento ou borda aplicada via CSS não é considerada.

  • É por isso que o nó de texto estático deve ser renderizado exatamente do mesmo tamanho que o nó de texto EmberJs.
  • Ou até um pouco maior, aumentando a line-height.
    Por exemplo, se a largura dos nós de texto não corresponder, há muitos casos geométricos introduzidos por diferentes quebras de linha onde o nó de texto estático se torna menor que o EmberJs.

Veja: Exemplos de LCP


Na verdade, usei a renderização noscript das postagens dentro de uma página de tópico. As classes CSS correspondem ligeiramente às reais - então a aparência é igual.

Veja: Alterações em app/views/layouts/application.html.erb

Editar: Minha culpa, esta é na verdade a visualização do crawler: app/views/topics/show.html.erb

2 curtidas

No POC existem duas funcionalidades combinadas - devemos dividi-las em duas flags de funcionalidade experimental?

  • JavaScript com tag defer (flag de funcionalidade no painel de configurações)
    (flag de funcionalidade oculta, pois uma reconstrução do container ou um flush do cache do tema é necessária para isso)Correção: alternância rápida com cache
  • Exibição de conteúdo estático na visualização do tópico (flag de funcionalidade no painel de configurações)

Aqui vamos nós: flags de funcionalidade


Claro, o impacto total no LCP é alcançado apenas usando ambos: FCP: conteúdo estático

Pode haver instâncias do Discourse onde plugins ou componentes de tema falham na deferência do JS. Ao dividir essas funcionalidades, elas podem ter um pequeno ganho no conteúdo estático sem adiar o JS: FCP: conteúdo estático sem adiar JS

2 curtidas

Primeira impressão da Google Search Console com o POC aplicado desde 30/01/2022:

Desktop

O Desktop demorou para que os resultados chegassem:


Observação: a antiga linha de base verde representa páginas da web não-Discourse no mesmo domínio.

Mobile


Observação: a antiga linha de base verde representa páginas da web não-Discourse no mesmo domínio.

Vamos esperar mais 7-14 dias para, esperançosamente, ver mais melhorias para páginas mobile, já que os valores são calculados com a média dos últimos 28 dias - contando apenas 12 dias com o POC aplicado no momento.

5 curtidas

Resumo do LCP sobre Prova de Conceito

A Prova de Conceito (POC) está em vigor desde 30/01/2022 e levou mais de 4 semanas para afetar todas as páginas no relatório “Core Web Vitals” do Google Search Console - com base nos dados do CrUX.

Todas as páginas de tópicos estão na zona verde do LCP (medido pelo CrUX):

  • Desktop: LCP 1,7 seg
  • Mobile: LCP 2,0 seg

Dados do LCP: Google Search Console/CrUX

Impressão do Google Search Console com a POC aplicada desde 30/01/2022:

Desktop


Nota: a antiga linha de base verde representa páginas da web não-Discourse no mesmo domínio.

Bons URLs

Mobile


Nota: a antiga linha de base verde representa páginas da web não-Discourse no mesmo domínio.

Bons URLs

Problema de LCP: maior que 2,5s (mobile)


Nota: Apenas as páginas de tópicos mostram conteúdo estático antes do conteúdo EmberJS


Aprovação de PR com feature flags necessária

@sam Você pode delegar este PR para alguém dar uma olhada para aprovação, por favor.

3 curtidas

Nós certamente revisaremos cuidadosamente, é uma mudança muito grande e pode levar um tempo para chegarmos a ela.

5 curtidas

@rrit obrigado por compartilhar os dados do seu site! Discutimos isso internamente e, receio que não adicionaremos essa funcionalidade ao Discourse core no momento.

Embora as métricas do Web Vital que você compartilhou sejam muito impressionantes, o flash de conteúdo de ‘visualização de crawler’ não proporciona uma boa experiência ao usuário. As alterações de estilo que você fez certamente ajudam, mas precisarão ser ajustadas para cada site Discourse que tiver estilo personalizado.

Nosso objetivo de longo prazo é implementar o renderização do lado do servidor real usando algo como o Ember FastBoot. Teoricamente, isso forneceria as mesmas melhorias estatísticas que você mediu, ao mesmo tempo que proporcionaria uma experiência de usuário perfeita. Preferiríamos focar nossos esforços nesse objetivo.


Dito isso, o Discourse é super extensível, então acho que seria totalmente possível implementar sua ideia em um plugin Discourse e compartilhá-lo aqui em Plugin.

A maior alteração que você fez no PR principal é adicionar o atributo defer às tags de script. Substituir todos esses locais de um plugin seria muito difícil. No entanto, acho que o mesmo resultado poderia ser alcançado com uma abordagem baseada em middleware. Encontrei este post de blog que descreve um problema semelhante:

Usando essa técnica, você poderia escrever um middleware que verifica respostas text/html, as analisa e, em seguida, adiciona atributos defer onde necessário.

Adicionar middleware a partir de um plugin pode ser feito algo como isto:

# name: meu-plugin
# about: Descrição do meu plugin
# version: 1.0
# url: https://example.org

require_relative "lib/script_defer_middleware"

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

Se você encontrar algum obstáculo com uma abordagem baseada em plugin, sinta-se à vontade para postar aqui e ficaremos felizes em tentar apontá-lo na direção certa.

10 curtidas

Se eu tiver tempo para isso, provavelmente implementarei um plugin.

Mas, por enquanto, tento me virar com uma abordagem de patch em web_only.yml:

# pseudo-código não testado!
hooks:
  after_code:
    - exec:
        cd: $home
        cmd:
          - curl https://patch-diff.githubusercontent.com/raw/discourse/discourse/pull/15858.diff | git apply
4 curtidas

Ember FastBoot parece uma abordagem de longo prazo perfeita. Enquanto isso, o tópico LCP continua quente:

2 curtidas

Obrigado por trabalhar nisso @rrit :+1:

Tenho boas notícias, implementamos um novo recurso no Discourse, que deve ajudar bastante com isso

5 curtidas