Como criar vários Progressive Web Apps no mesmo domínio

Como criar vários PWAs usando o mesmo nome de domínio para que os usuários saibam que pertencem à mesma organização ou serviço.

Chase Phillips
Demián Renzulli
Demián Renzulli
Matt Giuca
Matt Giuca

Na postagem do blog sobre Progressive Web Apps em sites de várias origens (link em inglês), Demian discutiu os desafios que os sites criados em várias origens enfrentam ao tentar criar um único App Web Progressivo que englobe todos eles.

Um exemplo desse tipo de arquitetura de site é um site de e-commerce em que:

  • A página inicial fica em https://www.example.com.
  • As páginas de categoria estão hospedadas em https://category.example.com.
  • As páginas de detalhes do produto em https://product.example.com.

Conforme discutido no artigo, a política de mesma origem impõe várias restrições, impedindo o compartilhamento de service workers, caches e permissões entre origens. Por esse motivo, é altamente recomendável evitar esse tipo de configuração e, para aqueles que já têm sites criados dessa forma, considerar a migração para uma única arquitetura de site de origem sempre que possível.

Diagrama mostrando um site dividido em várias origens e mostrando que essa técnica não é recomendada ao criar PWAs.
Evite usar origens diferentes para seções do mesmo site ao tentar criar um app da Web progressivo unificado.

Nesta postagem, veja o caso oposto: em vez de um único PWA em origens diferentes, analisaremos o caso de empresas que queiram fornecer vários PWAs, usando o mesmo nome de domínio, e informar o usuário que esses PWAs pertencem à mesma organização ou serviço.

Como você deve ter notado, estamos usando termos diferentes, mas inter-relacionados, como domínios e origens. Antes de continuar, vamos revisar esses conceitos.

Termos técnicos

  • Domínio: qualquer sequência de rótulos, conforme definido no Sistema de Nomes de Domínio (DNS). Por exemplo: com e example.com são domínios.
  • Nome do host:uma entrada DNS que é resolvida em pelo menos um endereço IP. Por exemplo: www.example.com seria um nome de host, example.com poderia ser um nome de host se tivesse um endereço IP, e com nunca seria resolvido como um endereço IP e, portanto, nunca poderia ser um nome de host.
  • Origem:uma combinação de esquema, nome do host e porta (opcionalmente). Por exemplo, https://www.example.com:443 é uma origem.

Como o nome indica, a política de mesma origem impõe restrições às origens. Por isso, faremos referência ao termo ao longo do artigo. No entanto, usaremos "domínios" ou "subdomínios" periodicamente para descrever a técnica usada e criar as diferentes "origens".

Em alguns casos, convém criar apps independentes, mas ainda identificá-los como pertencentes à mesma organização ou "marca". Reutilizar o mesmo nome de domínio é uma boa maneira de estabelecer essa relação. Exemplo:

  • Um site de e-commerce quer criar uma experiência independente para que os vendedores gerenciem o inventário, garantindo que eles entendam que ele pertence ao site principal em que os usuários compram produtos.
  • Um site de notícias esportivas quer criar um app específico para um grande evento esportivo, permitir que os usuários recebam estatísticas sobre as competições favoritas deles por notificações e o instalem como um App Web Progressivo, além de garantir que os usuários o reconheçam como um app criado pela empresa de notícias.
  • Uma empresa quer criar apps separados de chat, e-mail e agenda, e quer que eles funcionem como apps individuais, vinculados ao nome da empresa.
Evite usar origens diferentes para seções do mesmo site ao tentar criar um app da Web progressivo unificado.
A empresa proprietária de example.com quer fornecer três apps independentes ou PWAs, usando o mesmo nome de domínio para estabelecer a relação entre eles.

Como usar origens separadas

A abordagem recomendada em casos como esses é que cada app conceitualmente diferente está na própria origem.

Para usar o mesmo nome de domínio em todos eles, use subdomínios. Por exemplo, uma empresa que oferece vários apps ou serviços da Internet pode hospedar um app de e-mail em https://mail.example.com e um app de agenda em https://calendar.example.com enquanto oferece o serviço principal em https://www.example.com. Outro exemplo é um site de esportes que quer criar um app independente totalmente dedicado a um evento esportivo importante, como um campeonato de futebol no https://footballcup.example.com, que os usuários podem instalar e usar de forma independente do site principal do esporte, hospedado em https://www.example.com. Essa abordagem também pode ser útil para plataformas que permitem que os clientes criem apps independentes com a marca da empresa. Por exemplo, um app que permite que os comerciantes criem os próprios PWAs em https://merchant1.example.com, https://merchant2.example.com etc.

O uso de origens diferentes garante o isolamento entre os apps, o que significa que cada um deles pode gerenciar diferentes recursos do navegador de maneira independente, incluindo:

  • Capacidade de instalação:cada app tem o próprio manifesto e oferece a própria experiência instalável.
  • Armazenamento:cada app tem os próprios caches, armazenamento local e, basicamente, todas as formas de armazenamento local do dispositivo, sem compartilhar com os outros.
  • Service Workers: cada app tem o próprio service worker para os escopos registrados.
  • Permissões: as permissões também têm escopo por origens. Assim, os usuários saberão exatamente para qual serviço estão concedendo permissões, e recursos como notificações serão devidamente atribuídos a cada app.

Criar esse grau de isolamento é o mais desejável no caso de uso de vários PWAs independentes. Por isso, é altamente recomendável usar essa abordagem.

Se os aplicativos em subdomínios quiserem compartilhar dados locais entre si, ainda poderão fazer isso por meio de cookies ou, em cenários mais avançados, poderão considerar sincronizar o armazenamento por meio de um servidor.

ALT_TEXT_HERE
É recomendável criar PWAs diferentes em origens distintas usando subdomínios.

Usar a mesma origem

A segunda abordagem é criar diferentes PWAs na mesma origem. Isso inclui os seguintes cenários:

Caminhos não sobrepostos

Vários PWAs ou "apps da Web" conceituais, hospedados na mesma origem, com caminhos não sobrepostos. Exemplo:

  • https://example.com/app1/
  • https://example.com/app2/

Caminhos sobrepostos/aninhados

Vários PWAs na mesma origem, com um escopo aninhado dentro do outro:

  • https://example.com/ (o "app externo")
  • https://example.com/app/ (o "app interno")

A API do service worker e o formato do manifesto permitem realizar qualquer uma das ações acima, usando o escopo no nível do caminho. No entanto, em ambos os casos, usar a mesma origem apresenta muitos problemas e limitações. A raiz disso é o fato de que o navegador não os considera totalmente como "apps" distintos. Portanto, essa abordagem não é recomendada.

ALT_TEXT_HERE
Não é recomendável usar caminhos (sobrepostos ou não) para fornecer dois PWAs independentes ("app1", "app2") na mesma origem.

Na próxima seção, vamos analisar esses desafios em mais detalhes e analisar o que pode ser feito se o uso de origens separadas não for uma opção.

Desafios para vários PWAs de mesma origem

Aqui estão alguns problemas práticos comuns a ambas as abordagens de mesma origem:

  • Armazenamento:cookies, armazenamento local e todas as formas de armazenamento local do dispositivo são compartilhados entre os apps. Por esse motivo, se o usuário decidir apagar os dados locais de um app, todos os dados da origem serão excluídos. Não há como fazer isso para um único app. O Chrome e alguns outros navegadores solicitam ativamente que os usuários excluam os dados locais ao desinstalar um dos apps, o que também afetará os dados de outros apps na origem. Outro problema é que os apps também precisam compartilhar a cota de armazenamento, o que significa que, se um deles ocupar muito espaço, o outro será afetado negativamente.
  • Permissões:as permissões estão vinculadas à origem. Isso significa que, se o usuário conceder permissão para um app, ela será aplicada a todos os apps dessa origem simultaneamente. Isso pode parecer uma coisa boa (não ter que pedir uma permissão várias vezes), mas lembre-se: se o usuário bloquear a permissão para um app, isso vai impedir que os outros solicitem essa permissão ou usem esse recurso.
  • Configurações do usuário:as configurações também são definidas por origem. Por exemplo, se dois apps tiverem tamanhos de fonte diferentes e o usuário quiser ajustar o zoom em apenas um deles para compensar isso, não será possível fazer isso sem aplicar a configuração aos outros apps também.

Esses desafios dificultam o incentivo dessa abordagem. No entanto, se não for possível usar uma origem separada (por exemplo, um subdomínio), em vez de caminhos sobrepostos/aninhados, conforme discutido na seção Como usar origens separadas, é recomendável usar caminhos não sobrepostos.

Como mencionado, os desafios discutidos nesta seção são comuns para as duas abordagens de mesma origem. Na próxima seção, vamos nos aprofundar nos detalhes do motivo pelo qual o uso de caminhos sobrepostos/aninhados é a estratégia menos recomendada.

Outros desafios para caminhos sobrepostos/aninhados

O outro problema com a abordagem de caminhos sobrepostos/aninhados (em que https://example.com/ é o app externo e https://example.com/app/ é o app interno) é que todos os URLs no app interno são considerados parte do app externo e interno.

Na prática, isso apresenta os seguintes problemas:

  • Promoção de instalação:se o usuário acessar o app interno (por exemplo, em um navegador da Web), quando o app externo já estiver instalado no dispositivo do usuário, o navegador não mostrará os banners promocionais de instalação e o evento beforeInstallPrompt não será acionado. O motivo é que o navegador vai verificar e conferir se a página atual pertence a um app que já está instalado, e concluirá que ela é. A solução alternativa é instalar o app interno manualmente (usando a opção "Criar atalho" do menu do navegador) ou instalar o app interno primeiro, antes do app externo.
  • Notificação e a API Badging: se o app externo estiver instalado, mas o app interno não, as notificações e os selos do app interno serão atribuídos erroneamente ao app externo, que é o escopo mais próximo de um app instalado. Esse recurso funciona corretamente caso os dois apps estejam instalados no dispositivo do usuário.
  • Captura de link: o app externo pode capturar URLs que pertencem ao app interno. Isso é especialmente provável se o app externo estiver instalado, mas o interno não. Da mesma forma, os links dentro do app externo que levam ao app interno não vão vincular a captura ao app interno, já que são considerados dentro do escopo do app externo. Além disso, no ChromeOS e no Android, se esses apps forem adicionados à Play Store (como Atividades confiáveis na Web), o app externo vai capturar todos os links. Mesmo que o app interno esteja instalado, o SO ainda vai oferecer ao usuário a opção de abri-lo no app externo.

Conclusão

Neste artigo, analisamos as diferentes maneiras pelas quais os desenvolvedores podem criar vários apps da Web progressivos relacionados entre si no mesmo domínio.

Em resumo, recomendamos usar uma origem diferente (por exemplo, usando subdomínios) para hospedar PWAs independentes. Hospedar os apps na mesma origem apresenta muitos desafios, principalmente porque o navegador não os considera totalmente distintos.

  • Origens diferentes: recomendado
  • Mesma origem, caminhos não sobrepostos: não recomendado
  • Mesma origem, caminhos sobrepostos/aninhados: altamente não recomendado.

Se não for possível usar origens diferentes, com caminhos não sobrepostos (por exemplo, https://example.com/app1/ e https://example.com/app2/), é altamente recomendável em vez de caminhos sobrepostos ou aninhados, como https://example.com/ (para o app externo) e https://example.com/app/ (para o app interno).

Outros recursos

Agradecemos muito pelas sugestões e análises técnicas: Joe Medley, Dominick Ng, Alan Cutter, Daniel Murphy, Penny McLachlan, Thomas Steiner e Darwin Huang

Foto de Tim Mossholder no Unsplash (em inglês)