Siga o Papai Noel como um PWA

Ver o site

Resumo

O Siga o Papai Noel foi rapidamente atualizado para um Progressive Web App off-line na temporada de festas de fim de ano de 2016, graças em parte ao design de cena existente.

Resultados

  • Papai Noel é um Progressive Web App (PWA) compatível com adição à tela inicial (ATHS, na sigla em inglês) e off-line
  • 10% das sessões qualificadas começaram pelo ícone do ATHS
  • 75% dos usuários dão suporte nativo a elementos personalizados e ao shadow DOM, duas partes principais dos componentes da Web
  • Pontuação do Lighthouse de 81
  • O modo off-line, por meio da API Service Worker, está vinculado ao carregamento lento para armazenar em cache apenas as cenas visitadas e fazer upgrade silenciosamente em novas versões.

Contexto

O Siga o Papai Noel é uma tradição de fim de ano aqui no Google. Todos os anos, celebre a temporada com jogos e experiências educativas durante o mês de dezembro. Enquanto o Papai Noel tira uma folga merecida, os duendes trabalham para lançar o Siga o Papai Noel como código aberto, tanto na Web quanto no Android.

Na Web, o "Siga o Papai Noel" é um site grande e interativo com muitas "cenas" exclusivas, escritas usando o Polymer, que são compatíveis com a maioria dos navegadores modernos. A avaliação para saber se o navegador do usuário é "moderno" é determinada pela detecção de recursos: o Papai Noel exige o Set e a API Web Performance, entre outros.

Em 2016, atualizamos o mecanismo dos bastidores do Siga o Papai Noel para oferecer uma experiência off-line na maioria das cenas. Isso exclui cenas baseadas em vídeos do YouTube ou que mostrem o local ao vivo do Papai Noel, que está disponível apenas com uma conexão direta com o Polo Norte. 📶☃️

Siga o Papai Noel em um dispositivo Android
Siga o Papai Noel em um dispositivo Android

Desafios

O Papai Noel incorpora um design responsivo que funciona bem em celulares, tablets e computadores. O site adora um conteúdo multimídia excelente, incluindo visuais estilizados e áudio com tema festivo. No entanto, o Siga o Papai Noel tem centenas de megabytes. Isso ocorre por alguns motivos:

  • O Papai Noel é compatível com mais de 35 idiomas, por isso muitos recursos precisam ser duplicados.
  • Plataformas diferentes têm suporte de mídia diferente (por exemplo, mp3 versus ogg).
  • Às vezes, arquivos multimídia são fornecidos em tamanhos e resoluções diferentes.

Os duendes do Papai Noel também trabalham duro durante dezembro, muitas vezes lançando novas atualizações críticas durante todo o mês. Isso significa que os recursos já armazenados em cache pelo navegador do usuário talvez precisem ser atualizados em visitas repetidas.

Esses desafios:

  • Grandes recursos multimídia para diferentes "cenas"
  • Mudanças lançadas ao longo do mês

...resultam na inadequação de uma estratégia off-line ingênua.

Papai Noel, construído com polímero

Vale a pena recuar e falar sobre o design geral do Papai Noel antes de mergulhar em como nós o atualizamos para um PWA off-line.

Papai Noel é um aplicativo de página única, originalmente escrito no Polymer 0.5 e atualizado para o Polymer 1.7. O Papai Noel é composto por um conjunto compartilhado de códigos (o roteador, recursos de navegação compartilhados etc.). Ele também tem muitas "cenas" exclusivas.

Pré-carregador

Cada cena pode ser acessada por um URL diferente (/village.html, /codelab.html e /boatload.html) e consiste em um componente da Web próprio. Quando um usuário abre uma cena, pré-carregamos todos os HTML e recursos necessários (imagens, áudio, CSS, js), que existem em /scenes/[[sceneName]] no repositório do Siga o Papai Noel. Enquanto isso, os usuários veem um pré-carregador amigável que mostra o progresso.

Com essa abordagem, não precisamos carregar recursos desnecessários para cenas que o usuário não vê (que são muitos dados). Isso também significa que precisamos manter um "manifesto em cache" interno de todos os recursos necessários para cada cena. O manifesto de cache é um arquivo JSON que armazena um mapeamento do nome de arquivo para um hash MD5 do conteúdo.

Carregar o que você usa

Esse modelo economiza largura de banda, exibindo apenas recursos necessários para as cenas que um usuário visita, em vez de pré-carregar todo o site de uma vez. O Siga o Papai Noel aproveita a capacidade da Polymer de fazer upgrade de elementos personalizados no tempo de execução, não no tempo de carregamento. Considere o snippet a seguir:

<lazy-pages id="lazypages" selected-item="&#123;{selectedScene}}" ... >
    <dorf-scene id="village" route="village" icon="1f384" permanent
        mode$="[[mode]]"
        path$="scenes/dorf/dorf-scene_[[language]].html"
        class="santa-scene" allow-page-scrolling></dorf-scene>

    <boatload-scene route="boatload" icon="26f5"
        path$="scenes/boatload/boatload-scene_[[language]].html"
        loading-bg-color="#8fd7f7"
        loading-src="scenes/boatload/img/loading.svg"
        logo="scenes/boatload/img/logo.svg"
        class="santa-scene"></boatload-scene>

O Siga o Papai Noel segue estas etapas para carregar uma cena, por exemplo, boatload-scene:

  1. Todos os elementos da cena (incluindo <boatload-scene>) são inicialmente desconhecidos e todos são tratados como HTMLUnknownElement com alguns atributos extras.
  2. Quando a cena selecionada é alterada, o elemento <lazy-pages> é notificado.
  3. O elemento <lazy-pages> resolve o elemento da cena e o atributo path, carregando a importação HTML scenes/boatload/boatload-scene_en.html. Ele contém o elemento Polymer e seus elementos dependentes.
  4. O pré-carregador compatível é exibido.
  5. Depois que a importação de HTML é carregada e executada, o <boatload-scene> é atualizado de forma transparente para um elemento Polymer real, cheio de alegrias festivas. 🎄🎉

Essa abordagem tem seus desafios. Por exemplo, não queremos incluir componentes da Web duplicados. Se duas cenas usarem um elemento comum, por exemplo, paper-button, nós o removemos como parte do nosso processo de compilação e, em vez disso, o incluímos no código compartilhado do Papai Noel.

Design off-line

O Siga o Papai Noel já está segmentado em cenas perfeitamente graças ao Polymer e ao lazy-pages. Cada cena tem seu próprio diretório. Projetamos o service worker do Siga o Papai Noel, peça central que permite o acesso off-line, executado no navegador do usuário, para reconhecer a diferença entre código compartilhado e "cena".

Qual é a teoria por trás do service worker? Quando um usuário em um navegador compatível carrega seu site, o HTML do front-end pode solicitar que o service worker seja instalado. No Siga o Papai Noel, o service worker mora em /sw.js. Isso dispara um evento install que vai pré-armazenar em cache todo o código compartilhado do Papai Noel para que nada precise ser buscado no momento da execução.

Diagrama do fluxo de SO

Depois de instalado, o service worker pode interceptar todas as solicitações HTTP. Para o Siga o Papai Noel, o fluxo de decisão simplificado tem esta aparência:

  1. A solicitação já está armazenada em cache?
    • Ótimo! Retorne a resposta armazenada em cache.
  2. A solicitação corresponde a um diretório de cenário, como "things/boatload/boatload-scene_en.html"?
    • Realize uma solicitação de rede e armazene o resultado no cache antes de retorná-lo ao usuário.
  3. Caso contrário, execute uma solicitação de rede regular.

Nosso fluxo e evento install permitem que o Siga o Papai Noel seja carregado mesmo que o usuário esteja off-line. No entanto, somente as cenas que um usuário já carregou vão estar disponíveis. Isso é perfeito para repetir um jogo e superar sua maior pontuação.

Os observadores atentos podem perceber que nossa estratégia de armazenamento em cache não permite mudanças no conteúdo. Quando um arquivo é armazenado em cache no navegador do usuário, ele nunca é alterado. Vamos ver mais sobre isso depois.

Faremos isso ao vivo

Como mencionamos, os duendes do Papai Noel trabalham duro durante todo o mês de dezembro e, muitas vezes, precisam lançar novas atualizações durante todo o mês. Quando uma versão do Siga o Papai Noel é criada, ela recebe um rótulo exclusivo, por exemplo, v20161204112055, o carimbo de data/hora da versão (11:20:55 em 4 de dezembro de 2016).

Para essa versão rotulada, geramos um hash MD5 de cada arquivo e o armazenamos em nosso "manifesto em cache". Em um disco moderno de estado sólido, isso adiciona apenas alguns segundos ao processo de compilação.

Cada versão é implantada em um caminho exclusivo no servidor de armazenamento em cache estático do Google. Ou seja, as versões mais antigas nunca são removidas. Isso significa que, depois de uma nova versão, todos os recursos terão um URL diferente, mesmo que não tenham mudado, e qualquer item armazenado em cache pelo navegador ou pelo service worker será inútil, a menos que façamos algum trabalho extra.

Também implantamos uma nova versão do que chamamos de recursos "prod", ou seja, o HTML do índice do Papai Noel e o service worker, que fica disponível em https://santatracker.google.com/. Isso substitui a versão antiga.

Diagrama estático

Sempre que o Siga o Papai Noel é carregado, o navegador verifica se há um service worker atualizado e faz a busca dele, se disponível. No nosso caso, cada versão gera um código diferente em bytes. O navegador considera isso como um upgrade e executa um novo evento install.

Nesse ponto, os navegadores do usuário analisam o novo "manifesto de cache". Isso será comparado ao cache atual do usuário e, se os recursos tiverem um hash MD5 diferente, eles serão excluídos do cache e solicitaremos que o navegador faça uma nova busca. No entanto, na maioria dos casos, o conteúdo armazenado em cache é basicamente o mesmo ou tem apenas pequenas diferenças.

Diagrama de cache

No Siga o Papai Noel, fazer upgrade do service worker faz com que o navegador do usuário seja atualizado imediatamente.

Experiência de navegação off-line

Também foi necessário fazer algumas mudanças na interface para oferecer uma experiência off-line e facilitar a compreensão dos usuários que não esperam que um site funcione off-line.

Um banner pequeno informa quando você está navegando off-line. Todas as cenas que não são armazenadas em cache são "congeladas" e não podem ser clicadas. Dessa forma, eles perdem o acesso ao conteúdo indisponível.

Off-line

O Siga o Papai Noel faz solicitações regulares à API do Papai Noel. Se essas solicitações falharem ou atingirem o tempo limite, presumiremos que o usuário está off-line. Usamos essa API em vez da propriedade navigator.onLine integrada do navegador: isso apenas nos informa se o usuário está on-line. Isso também é conhecido como Lie-Fi.

A conexão internacional

Enquanto a maioria dos nossos usuários está em inglês (seguido pelo japonês, português, espanhol e francês), o Papai Noel é construído e lançado em mais de 35 idiomas diferentes.

Quando um usuário carrega o Siga o Papai Noel, usamos o idioma do navegador e outras dicas para escolher um idioma para servir. A maioria dos usuários nunca substitui esse idioma. No entanto, se um usuário escolhe um novo idioma usando nosso seletor, tratamos isso como se um upgrade estivesse disponível, como no caso acima, quando uma nova versão do Siga o Papai Noel está disponível.

linguagem

Em outras palavras, a versão atual do Siga o Papai Noel para os fins do service worker é, na verdade, uma tupla de (build,language).

Adicionar à tela inicial

Como o Papai Noel trabalha off-line e oferece um service worker, os usuários qualificados são solicitados a instalá-lo na tela inicial. Em 2016, cerca de 10% dos carregamentos qualificados eram do ícone da tela inicial.

Conclusão

Conseguimos converter rapidamente o Siga o Papai Noel em um PWA off-line, proporcionando uma experiência confiável e envolvente graças ao design de cenário atual, que foi feito com o Polymer e os componentes da Web. Ele também aproveita nosso sistema de build para realizar upgrades eficientes, invalidando apenas os recursos alterados.

Embora o Papai Noel seja uma solução personalizada, muitos dos princípios dela podem ser encontrados no App Toolbox do projeto Polymer. Sugerimos que você faça isso se estiver criando um novo PWA do zero ou, se estiver procurando uma abordagem independente de framework, use a Biblioteca Workbox.