Arquitetura

Para criar o aplicativo e aproveitar ao máximo a tecnologia que torna os PWAs confiáveis, instaláveis e capazes de fazer, você precisa entender o aplicativo e as restrições, além de escolher uma arquitetura adequada para ambos.

SPA x MPA

Atualmente, há dois padrões de arquitetura principais no desenvolvimento da Web: apps de página única, ou SPAs, e apps de várias páginas, ou MPAs.

Os apps de página única são definidos porque o JavaScript do lado do cliente controla a maior parte ou toda a renderização HTML de uma página com base nos dados recuperados ou fornecidos ao app. O app substitui a navegação integrada do navegador, substituindo-a pela funcionalidade de roteamento e processamento de visualização.

Os aplicativos de várias páginas geralmente têm HTML pré-renderizado enviado diretamente ao navegador, muitas vezes aprimorado com JavaScript do lado do cliente após o navegador terminar de carregar o HTML. Além disso, eles dependem dos mecanismos de navegação integrados do navegador para exibir visualizações subsequentes.

Ambas as arquiteturas podem ser usadas para criar PWAs.

Cada um tem vantagens e desvantagens, e selecionar o caminho certo para seu caso de uso e contexto é fundamental para proporcionar uma experiência rápida e confiável aos usuários.

Apps de página única

Prós
  • Principalmente atualizações na página atômicas.
  • Dependências do lado do cliente carregadas na inicialização.
  • Os carregamentos subsequentes são rápidos devido ao uso do cache.
Contras
  • Alto custo de carregamento inicial.
  • O desempenho depende do hardware do dispositivo e da conexão de rede.
  • É necessária uma complexidade adicional para o aplicativo.

Os apps de página única são uma boa opção de arquitetura se:

  • A interação do usuário é centrada principalmente em atualizações atômicas de dados interconectados exibidos na mesma página, por exemplo, em um painel de dados em tempo real ou em um app de edição de vídeo.
  • Seu aplicativo tem dependências de inicialização somente no lado do cliente, por exemplo, um provedor de autenticação terceirizado com um custo de inicialização extremamente alto.
  • Os dados necessários para que uma visualização seja carregada dependem de um contexto específico somente no lado do cliente, por exemplo, exibição de controles para um hardware conectado.
  • O app é pequeno e simples o suficiente para que o tamanho e a complexidade dele não afetem os contras listados acima.

Os SPAs podem não ser uma boa opção de arquitetura nos seguintes casos:

  • O desempenho do carregamento inicial é essencial. Os SPAs geralmente precisam carregar mais JavaScript para determinar o que será carregado e como exibi-lo. O tempo de análise e execução desse JavaScript, combinado com a recuperação de conteúdo, é mais lento do que o envio do HTML renderizado.
  • O app é executado principalmente em dispositivos de baixa potência para média. Como os SPAs dependem de JavaScript para renderização, a experiência do usuário depende muito mais da capacidade de seu dispositivo específico do que de um MPA.

Como os SPAs precisam substituir a navegação integrada do navegador pelo roteamento, eles exigem um nível mínimo de complexidade para atualizar a visualização atual, gerenciar mudanças de navegação e limpar visualizações anteriores que, de outra forma, seriam manipuladas pelo navegador, dificultando a manutenção geral e exigindo mais carga para o dispositivo do usuário.

Apps com várias páginas

Prós
  • Principalmente atualizações de página inteira.
  • A velocidade de renderização inicial é fundamental.
  • O script do lado do cliente pode ser uma melhoria.
Contras
  • As visualizações secundárias exigem outra chamada do servidor.
  • O contexto não é transferido entre as visualizações.
  • Requer um servidor ou uma pré-renderização.

Apps de várias páginas são uma boa opção de arquitetura se:

  • A interação do usuário é centrada principalmente em visualizações de um único dado com dados opcionais baseados em contexto, por exemplo, um app de notícias ou de e-commerce.
  • A velocidade de renderização inicial é fundamental, já que enviar HTML já renderizado para o navegador é mais rápido do que montá-lo a partir de uma solicitação de dados após o carregamento, a análise e a execução de uma alternativa baseada em JavaScript.
  • A interatividade ou o contexto do lado do cliente pode ser incluído como uma melhoria após o carregamento inicial. Por exemplo, a inclusão de um perfil em uma página renderizada ou a adição de componentes secundários dependentes do contexto do lado do cliente.

As MPAs podem não ser uma boa escolha de arquitetura se:

  • Fazer o download novamente, analisar e executar novamente seu JavaScript ou CSS é extremamente caro. Essa condição é atenuada ao usar PWAs com service workers.
  • O contexto do lado do cliente, como a localização do usuário, não é transferido com perfeição entre as visualizações, e conseguir esse contexto novamente pode ser caro. Ele precisa ser capturado e recuperado ou novamente solicitado entre visualizações.

Porque visualizações individuais precisam ser renderizadas dinamicamente por um servidor ou pré-renderizadas antes do acesso, o que limita a hospedagem ou o aumento da complexidade dos dados.

Qual você escolher?

Mesmo com esses prós e contras, ambas as arquiteturas são válidas para criar seu PWA. É possível até misturá-las para diferentes partes do seu app, dependendo das necessidades dele. Por exemplo, fazer com que as páginas "Detalhes do app" sigam uma arquitetura de MPA e o fluxo de finalização de compra siga uma arquitetura de SPA.

Seja qual for a escolha, o próximo passo é entender como melhor usar os service workers para proporcionar a melhor experiência.

O poder do service worker

O service worker tem muito poder além do roteamento básico e entrega de respostas em cache e de rede. Podemos criar algoritmos complexos que podem melhorar a experiência e o desempenho do usuário.

Service Workers inclusive (SWI)

Um padrão emergente para usar service workers como parte integrante da arquitetura de um local é o Service Worker inclusive (SWI). O SWI divide os recursos individuais, geralmente uma página HTML, em partes com base nas necessidades de armazenamento em cache. Depois, os agrupa novamente no service worker para melhorar a consistência, o desempenho e a confiabilidade, reduzindo o tamanho do cache. Um site com um cabeçalho global, uma área de conteúdo, uma barra lateral e um rodapé.

Esta imagem é um exemplo de página da Web. Ela tem cinco seções diferentes que dividem a página em:

  • Layout geral.
  • Cabeçalho global (barra escura na parte superior).
  • Área de conteúdo (linhas à esquerda no meio e imagem).
  • Barra lateral (barra alta em tom escuro médio no meio à direita).
  • Rodapé (barra inferior escura).

Layout geral

O layout geral provavelmente não vai mudar com frequência e não tem dependências. Ele é uma boa opção para pré-armazenamento em cache.

O cabeçalho e o rodapé globais contêm itens como o menu superior e o rodapé do site, e apresentam um desafio específico: se a página fosse armazenada em cache como um todo, isso poderá mudar entre os carregamentos de página, dependendo de quando a página foi armazenada em cache.

Ao separar e armazenar em cache independentemente do conteúdo, você garante que os usuários sempre recebam a mesma versão, independentemente de quando estiverem armazenados em cache. Como são atualizados com pouca frequência, também são bons candidatos para o pré-armazenamento em cache. No entanto, eles têm uma dependência: o CSS e o JavaScript do site.

CSS e JavaScript

O ideal é que o CSS e o JavaScript do site sejam armazenados em cache com uma estratégia desatualizada de revalidação para permitir atualizações incrementais sem precisar atualizar o service worker, como acontece com recursos pré-armazenados em cache. Ainda assim, eles também precisam ser mantidos na versão mínima sempre que o service worker é atualizado com um novo cabeçalho ou rodapé global. Por isso, o cache também precisa ser atualizado com a versão mais recente dos recursos quando o service worker é instalado.

Área de conteúdo

Em seguida vem a área de conteúdo. Dependendo da frequência das atualizações, uma boa estratégia é usar a rede primeiro ou ficar desatualizada durante a revalidação. As imagens precisam ser armazenadas em cache com uma estratégia que priorize o cache, conforme discutido anteriormente.

Por fim, presumindo que o conteúdo da barra lateral contém conteúdo secundário, como tags e itens relacionados, isso não é essencial o suficiente para extrair da rede. Uma estratégia de revalidação funciona para isso.

Depois de passar por tudo isso, você pode estar pensando que só é possível fazer esse tipo de armazenamento em cache por seção para aplicativos de página única. Mas, ao adotar padrões inspirados em inclusões de borda ou inclusões no lado do servidor no service worker, com alguns recursos avançados desse tipo de machine learning, é possível fazer isso em qualquer arquitetura.

Sua vez de tentar

É possível testar os inclusões do service worker com o próximo codelab:

Respostas de streaming

A página anterior poderia ser criada usando o modelo de shell do app no mundo do SPA, onde o shell do app é armazenado em cache e exibido, e o conteúdo é carregado no lado do cliente. Com a introdução e a ampla disponibilidade da API Streams, tanto o shell do aplicativo quanto o conteúdo podem ser combinados no service worker e transmitidos para o navegador, oferecendo a flexibilidade de armazenamento em cache do shell do aplicativo com a velocidade dos MPAs.

Isso acontece porque:

  • Os streams podem ser criados de maneira assíncrona, permitindo que diferentes partes de um stream venham de outras origens.
  • O solicitante de um stream pode começar a trabalhar na resposta assim que o primeiro bloco de dados estiver disponível, em vez de esperar que todo o item seja concluído.
  • Analisadores otimizados para streaming, incluindo o navegador, podem exibir progressivamente o conteúdo do stream antes que ele seja concluído, acelerando o desempenho percebido da resposta.

Graças a essas três propriedades de streams, as arquiteturas construídas com base em streaming geralmente têm uma percepção de desempenho mais rápida do que as que não têm.

Trabalhar com a API Streams pode ser um desafio, já que ela é complexa e de baixo nível. Felizmente, há um módulo de caixa de trabalho que pode ajudar a configurar respostas de streaming para seus service workers.

Domínios, origens e escopo do PWA

Os Web workers, incluindo os service workers, o armazenamento e até mesmo a janela de um PWA instalado, são todos regidos por um dos mecanismos de segurança mais importantes da Web: a política de mesma origem. Dentro da mesma origem, as permissões são concedidas, os dados podem ser compartilhados e o service worker pode se comunicar com clientes diferentes. Fora da mesma origem, as permissões não são concedidas automaticamente, e os dados ficam isolados e inacessíveis entre origens diferentes.

Política de mesma origem

Dois URLs são definidos como tendo origem exata se o protocolo, a porta e o host forem os mesmos.

Por exemplo: https://squoosh.app e https://squoosh.app/v2 têm a mesma origem, mas http://squoosh.app, https://squoosh.com, https://app.squoosh.app e https://squoosh.app:8080 estão em origens diferentes. Consulte a referência da MDN da política de mesma origem para mais informações e exemplos.

Alterar subdomínios não é a única maneira de um host mudar. Cada host é composto por um domínio de nível superior (TLD), um domínio de nível secundário (SLD) e zero ou mais rótulos (às vezes chamados de subdomínios), separados por pontos e lidos da direita para a esquerda em um URL. Uma alteração em qualquer um dos itens resulta em um host diferente.

No módulo de gerenciamento de janelas, já vimos a aparência do navegador no app quando um usuário navega para uma origem diferente de um PWA instalado.

Esse navegador no app vai aparecer mesmo que os sites tenham o mesmo TLD e SLD, mas com rótulos diferentes, porque são considerados origens diferentes.

Um dos aspectos principais de uma origem em um contexto de navegação na Web é a forma como o armazenamento e as permissões funcionam. Uma origem compartilha muitos recursos entre todo o conteúdo e PWAs dentro dela, incluindo:

  • Cota e dados de armazenamento (IndexedDB, cookies, armazenamento da Web, armazenamento em cache).
  • Registros de service worker.
  • Permissões concedidas ou negadas (como push da Web, geolocalização, sensores).
  • Registros de push da Web.

Quando você passa de uma origem para outra, todo o acesso anterior é revogado. Portanto, as permissões precisam ser concedidas novamente, e o PWA não consegue acessar todos os dados salvos no armazenamento.

Recursos