Um guia para iniciantes para aplicativos da Web progressivos
Publicados: 2022-03-10Os aplicativos da web progressivos podem ser a próxima grande novidade para a web móvel. Originalmente propostos pelo Google em 2015, eles já atraíram bastante atenção pela relativa facilidade de desenvolvimento e pelas vitórias quase instantâneas para a experiência do usuário do aplicativo.
Leitura adicional no SmashingMag:
- Os blocos de construção dos aplicativos da Web progressivos
- Fundamentos de design conversacional: dicas para construir um chatbot
- Construindo um aplicativo de primeira classe que alavanca seu site
- Criando um aplicativo Web completo no Foundation For Apps
Um aplicativo da Web progressivo aproveita as tecnologias mais recentes para combinar o melhor dos aplicativos da Web e móveis . Pense nisso como um site construído usando tecnologias da web, mas que funciona e parece um aplicativo. Avanços recentes no navegador e na disponibilidade de service workers e nas APIs de Cache e Push permitiram que os desenvolvedores da Web permitissem aos usuários instalar aplicativos da Web em sua tela inicial, receber notificações por push e até mesmo trabalhar offline.
Os aplicativos da Web progressivos aproveitam o ecossistema, os plug-ins e a comunidade da Web muito maiores e a relativa facilidade de implantação e manutenção de um site quando comparado a um aplicativo nativo nas respectivas lojas de aplicativos. Para aqueles que desenvolvem tanto em dispositivos móveis quanto na web, você apreciará que um site pode ser construído em menos tempo, que uma API não precisa ser mantida com compatibilidade com versões anteriores (todos os usuários executarão a mesma versão do seu site código, ao contrário da fragmentação de versão de aplicativos nativos) e que o aplicativo geralmente será mais fácil de implantar e manter .
Por que aplicativos da Web progressivos?
Um estudo mostrou que, em média, um aplicativo perde 20% de seus usuários a cada passo entre o primeiro contato do usuário com o aplicativo e o início do uso do aplicativo. Um usuário deve primeiro encontrar o aplicativo em uma loja de aplicativos, baixá-lo, instalá-lo e, finalmente, abri-lo. Quando um usuário encontra seu aplicativo da web progressivo, ele poderá começar a usá-lo imediatamente, eliminando os estágios desnecessários de download e instalação. E quando o usuário retornar ao aplicativo, ele será solicitado a instalar o aplicativo e atualizar para uma experiência de tela cheia.
No entanto, um aplicativo nativo definitivamente não é de todo ruim. Aplicativos móveis com notificações push alcançam até três vezes mais retenção do que seus equivalentes sem push, e um usuário tem três vezes mais chances de reabrir um aplicativo móvel do que um site. Além disso, um aplicativo móvel bem projetado consome menos dados e é muito mais rápido porque alguns recursos residem no dispositivo.
Um aplicativo da Web progressivo aproveita as características de um aplicativo móvel, resultando em melhor retenção e desempenho do usuário, sem as complicações envolvidas na manutenção de um aplicativo móvel.
Casos de uso
Quando você deve criar um aplicativo da Web progressivo? O nativo geralmente é recomendado para aplicativos aos quais você espera que os usuários retornem com frequência, e um aplicativo da Web progressivo não é diferente. A Flipkart usa um aplicativo da web progressivo para sua popular plataforma de comércio eletrônico, Flipkart Lite, e a SBB usa um aplicativo da web progressivo para seu processo de check-in online, permitindo que os usuários acessem seus ingressos sem uma conexão com a Internet.
Ao avaliar se seu próximo aplicativo deve ser um aplicativo da Web progressivo, um site ou um aplicativo móvel nativo, primeiro identifique seus usuários e as ações mais importantes do usuário. Sendo “progressivo”, um aplicativo da web progressivo funciona em todos os navegadores, e a experiência é aprimorada sempre que o navegador do usuário é atualizado com recursos e APIs novos e aprimorados.
Assim, não há comprometimento na experiência do usuário com um aplicativo da web progressivo em comparação com um site tradicional; no entanto, você pode ter que decidir qual funcionalidade suportar offline e terá que facilitar a navegação (lembre-se que no modo autônomo, o usuário não tem acesso ao botão voltar). Se o seu site já tiver uma interface semelhante a um aplicativo, aplicar os conceitos de aplicativos da Web progressivos só o tornará melhor .
Se determinados recursos são necessários para ações críticas do usuário, mas ainda não estão disponíveis devido à falta de suporte entre navegadores, um aplicativo móvel nativo pode ser a melhor opção, garantindo a mesma experiência para todos os usuários.
Características de um aplicativo Web progressivo
Antes de entrarmos no código, é importante entender que os aplicativos da web progressivos têm as seguintes características:
- Progressivo . Por definição, um Progressive Web App deve funcionar em qualquer dispositivo e ser aprimorado progressivamente, aproveitando todos os recursos disponíveis no dispositivo e no navegador do usuário.
- Detectável . Como um aplicativo da Web progressivo é um site, ele deve ser descoberto nos mecanismos de pesquisa. Essa é uma grande vantagem em relação aos aplicativos nativos, que ainda ficam atrás dos sites em pesquisa.
- Linkável . Como outra característica herdada dos sites, um site bem projetado deve usar o URI para indicar o estado atual do aplicativo. Isso permitirá que o aplicativo da Web retenha ou recarregue seu estado quando o usuário marcar ou compartilhar o URL do aplicativo.
- Responsivo . A interface do usuário de um aplicativo da Web progressivo deve se ajustar ao formato e ao tamanho da tela do dispositivo.
- Semelhante a um aplicativo . Um aplicativo da Web progressivo deve se parecer com um aplicativo nativo e ser construído no modelo de shell do aplicativo, com atualizações mínimas de página.
- Independente de conectividade . Deve funcionar em áreas de baixa conectividade ou offline (nossa característica favorita).
- Reativável . Os usuários de aplicativos móveis são mais propensos a reutilizar seus aplicativos, e os aplicativos da Web progressivos visam atingir os mesmos objetivos por meio de recursos como notificações por push.
- Instalável . Um aplicativo da web progressivo pode ser instalado na tela inicial do dispositivo, tornando-o prontamente disponível.
- Fresco . Quando um novo conteúdo é publicado e o usuário está conectado à Internet, esse conteúdo deve ser disponibilizado no aplicativo.
- Seguro . Como um aplicativo da Web progressivo tem uma experiência de usuário mais íntima e como todas as solicitações de rede podem ser interceptadas por meio de service workers, é imperativo que o aplicativo seja hospedado em HTTPS para evitar ataques man-in-the-middle.
Vamos Codificar!
Nosso primeiro aplicativo web progressivo, Sky High, simulará a programação de chegadas de um aeroporto. Na primeira vez que o usuário acessar nosso aplicativo da web, queremos mostrar a ele uma lista dos próximos voos, recuperada de uma API. Se o usuário não tiver uma conexão com a Internet e recarregar o aplicativo da web, queremos mostrar a ele o horário do voo como estava no último download com uma conexão.
O básico
A primeira característica de um Progressive Web App é que ele deve funcionar em todos os dispositivos e deve ser aprimorado em dispositivos e navegadores que o permitam. Portanto, construímos nosso site usando HTML5 tradicional e com JavaScript que simula a recuperação de dados de uma API simulada. Em todo o aplicativo, estamos usando pequenos pedaços de Knockout para lidar com nossas associações Model-View-ViewModel (MVVM) — uma estrutura JavaScript leve que nos permite vincular nossos modelos JavaScript às nossas visualizações HTML. Optamos por usar o Knockout porque é relativamente simples de entender e não sobrecarrega o código; no entanto, você pode substituí-lo por qualquer outro framework, como React ou AngularJS.
Nosso site segue as diretrizes de design de materiais do Google, um conjunto de princípios que orientam o design e a interação. O design de materiais não serve apenas como um padrão unificado entre aplicativos e dispositivos, mas também dá significado ao design. Usamos o design de material para a visualização de chegadas do Sky High para dar ao nosso aplicativo da Web progressivo aquela aparência de aplicativo nativo.
Por fim, testamos nosso aplicativo para garantir que não haja instabilidade e que a rolagem seja suave como seda. A renderização sem Jank demonstrou melhorar o envolvimento do usuário. Apontar para uma renderização de 60 quadros por segundo.
Para esta demonstração, recuperaremos um arquivo JSON estático, em vez de uma API real. Isto é meramente para manter as coisas simples. No mundo real, você consultaria uma API ou usaria WebSockets.
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sky-High Airport Arrivals</title> <link async rel="stylesheet" href="./css/style.css"> <link href="https://fonts.googleapis.com/css?family=Roboto:300,600,300italic,600italic" rel="stylesheet" type="text/css"> </head> <body> <header> <div class="content"> <h3>Arrivals</h3> </div> </header> <div class="container"> <div class="content"> <ul class="arrivals-list" data-bind="foreach: arrivals"> <li class="item"> <span class="title" data-bind="html: title"></span> <span class="status" data-bind="html: status"></span> <span class="time" data-bind="html: time"></span> </li> </ul> </div> </div> <script src="./js/build/vendor.min.js"></script> <script src="./js/build/script.min.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sky-High Airport Arrivals</title> <link async rel="stylesheet" href="./css/style.css"> <link href="https://fonts.googleapis.com/css?family=Roboto:300,600,300italic,600italic" rel="stylesheet" type="text/css"> </head> <body> <header> <div class="content"> <h3>Arrivals</h3> </div> </header> <div class="container"> <div class="content"> <ul class="arrivals-list" data-bind="foreach: arrivals"> <li class="item"> <span class="title" data-bind="html: title"></span> <span class="status" data-bind="html: status"></span> <span class="time" data-bind="html: time"></span> </li> </ul> </div> </div> <script src="./js/build/vendor.min.js"></script> <script src="./js/build/script.min.js"></script> </body> </html>
O arquivo index.html
é relativamente padrão. Criamos uma lista HTML e vinculamos nossas arrivals
da propriedade View Model a ela usando Knockout, por meio do atributo data-bind=“foreach: arrivals”
. As arrivals
do View Model são declaradas no arquivo page.js
abaixo e expostas no módulo Page
. Em nossa página HTML, para cada item na matriz de arrivals
, vinculamos as propriedades title
, status
e time
à visualização HTML.
page.js
(var Page = (function() { // declare the view model used within the page function ViewModel() { var self = this; self.arrivals = ko.observableArray([]); } // expose the view model through the Page module return { vm: new ViewModel(), hideOfflineWarning: function() { // enable the live data document.querySelector(".arrivals-list").classList.remove('loading') // remove the offline message document.getElementById("offline").remove(); // load the live data }, showOfflineWarning: function() { // disable the live data document.querySelector(".arrivals-list").classList.add('loading') // load html template informing the user they are offline var request = new XMLHttpRequest(); request.open('GET', './offline.html', true); request.onload = function() { if (request.status === 200) { // success // create offline element with HTML loaded from offline.html template var offlineMessageElement = document.createElement("div"); offlineMessageElement.setAttribute("id", "offline"); offlineMessageElement.innerHTML = request.responseText; document.getElementById("main").appendChild(offlineMessageElement); } else { // error retrieving file console.warn('Error retrieving offline.html'); } }; request.onerror = function() { // network errors console.error('Connection error'); }; request.send(); } } })();
Este arquivo page.js
expõe o módulo Page
, que contém nosso ViewModel vm
e duas funções, hideOfflineWarning
e showOfflineWarning
. O View Model ViewModel
é um literal JavaScript simples que será usado em todo o aplicativo. As arrivals
de propriedade no ViewModel são observableArray
do Knockout, que liga automaticamente nosso HTML a um array JavaScript, permitindo-nos enviar e pop itens em nosso array em JavaScript e atualizar automaticamente o HTML da página.
As funções hideOfflineWarning
e showOfflineWarning
permitem que o restante do nosso aplicativo chame essas funções para atualizar a interface do usuário da página que exibe se estamos conectados online. O showOfflineWarning
adiciona uma classe de loading
ao nosso elemento HTML arrivals-list
para desbotar a lista e, em seguida, recupera o arquivo HTML offline.html
por meio do XHR. Assumindo que o arquivo foi recuperado com sucesso ( response.status === 200
), anexamos isso ao nosso HTML. Obviamente, se não estivermos usando service workers e o usuário não estiver conectado à Internet, não será possível recuperar offline.html
e, portanto, o usuário verá a página offline do navegador.
A lógica de negócios de onde recuperamos os dados de nossa API e os vinculamos a nossos modelos de visualização e visualizações é encontrada em arrivals.js
e é a funcionalidade padrão do MVVM usando o Knockout. No arquivo arrivals.js
, simplesmente inicializamos os serviços e os View Models que usaremos em toda a aplicação e expomos uma função — Arrivals.loadData()
— que recupera os dados e os vincula ao view model.
Manifesto do aplicativo da Web
Vamos tornar nosso aplicativo da web mais parecido com um aplicativo. Um arquivo de manifesto de aplicativo da Web é um arquivo JSON simples que segue a especificação do W3C. Com ele, é possível executar o aplicativo web em modo de tela cheia como um aplicativo autônomo, atribuir um ícone que será exibido quando o aplicativo for instalado no dispositivo e atribuir um tema e cor de fundo ao aplicativo. Além disso, o Chrome no Android sugerirá proativamente que o usuário instale o aplicativo da web por meio de um banner de instalação do aplicativo da web. Para exibir o prompt de instalação, seu aplicativo Web precisa:
- tem um arquivo de manifesto de aplicativo da web válido,
- ser servido por HTTPS,
- ter um service worker válido registrado,
- foram visitados duas vezes, com pelo menos cinco minutos entre cada visita.
manifest.json
{ "short_name": "Arrivals", "name": "Arrivals at Sky High", "description": "Progressive web application demonstration", "icons": [ { "src": "launcher-icon.png", "sizes": "48x48", "type": "image/png" }, { "src": "launcher-icon-96.png", "sizes": "96x96", "type": "image/png" }, { "src": "launcher-icon-144.png", "sizes": "144x144", "type": "image/png" }, { "src": "launcher-icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "launcher-icon-256.png", "sizes": "256x256", "type": "image/png" } ], "start_url": "./?utm_source=web_app_manifest", "display": "standalone", "orientation": "portrait", "theme_color": "#29BDBB", "background_color": "#29BDBB" }
Vamos detalhar este arquivo de manifesto:
-
short_name
é um nome legível para o aplicativo. No Chrome para Android, esse também é o nome que acompanha o ícone na tela inicial. -
name
também é um nome legível para o aplicativo e define como o aplicativo será listado. -
description
fornece uma descrição geral do aplicativo da web. -
icons
define uma matriz de imagens de tamanhos variados que servirão como o conjunto de ícones do aplicativo. No Chrome para Android, o ícone será usado na tela inicial, na tela inicial e no alternador de tarefas. -
start_url
é a URL inicial do aplicativo. -
display
define o modo de exibição padrão para o aplicativo da web:fullscreen
,standalone
,minimal-ui
oubrowser
. -
orientation
define a orientação padrão para o aplicativo da Web:portrait
oulandscape
. -
theme_color
é a cor de tema padrão para o aplicativo. No Android, isso também é usado para colorir a barra de status. -
background_color
define a cor de fundo do aplicativo da web. No Chrome, também define a cor de fundo da tela inicial. -
related_applications
não é implementado em nosso exemplo, mas é usado para especificar alternativas de aplicativos nativos nas várias lojas de aplicativos.
Adicione a referência manifest.json
à tag head
do arquivo index.html
:
<link rel="manifest" href="./manifest.json">
Depois que um usuário adicionar o aplicativo da Web à tela inicial, ele poderá se envolver novamente com seu aplicativo imediatamente a partir do dispositivo, sem precisar abrir o navegador diretamente. Você pode ver como isso é muito mais do que um marcador da web.
Adicionar à tela inicial no Chrome para Android da Smashing Magazine no Vimeo.
Trabalhadores de serviço
Um dos aspectos mais interessantes dos aplicativos da Web progressivos é que eles podem funcionar offline. Usando service workers, é possível mostrar dados que foram recuperados em sessões anteriores do aplicativo (usando IndexedDB) ou, alternativamente, mostrar o shell do aplicativo e informar ao usuário que ele não está conectado à Internet (a abordagem que temos tirada nesta demonstração). Depois que o usuário se reconectar, podemos recuperar os dados mais recentes do servidor.
Tudo isso é possível por meio de service workers, que são scripts orientados a eventos (escritos em JavaScript) que têm acesso a eventos de todo o domínio, incluindo buscas de rede. Com eles, podemos armazenar em cache todos os recursos estáticos, o que pode reduzir drasticamente as solicitações de rede e melhorar consideravelmente o desempenho também.
Shell do aplicativo
O shell do aplicativo é o mínimo de HTML, CSS e JavaScript necessário para alimentar uma interface de usuário. Um aplicativo móvel nativo inclui o shell do aplicativo como parte de sua distribuição, enquanto os sites normalmente solicitam isso pela rede. Os aplicativos da Web progressivos preenchem essa lacuna colocando os recursos e ativos do shell do aplicativo no cache do navegador. Em nosso aplicativo Sky High, podemos ver que nosso shell de aplicativo consiste na barra de cabeçalho superior, as fontes e qualquer CSS necessário para renderizá-los de forma elegante.
Para começar com os service workers, primeiro precisamos criar o arquivo JavaScript do service worker, sw.js
, colocado no diretório raiz.
sw.js
// Use a cacheName for cache versioning var cacheName = 'v1:static'; // During the installation phase, you'll usually want to cache static assets. self.addEventListener('install', function(e) { // Once the service worker is installed, go ahead and fetch the resources to make this work offline. e.waitUntil( caches.open(cacheName).then(function(cache) { return cache.addAll([ './', './css/style.css', './js/build/script.min.js', './js/build/vendor.min.js', './css/fonts/roboto.woff', './offline.html' ]).then(function() { self.skipWaiting(); }); }) ); }); // when the browser fetches a URL… self.addEventListener('fetch', function(event) { // … either respond with the cached object or go ahead and fetch the actual URL event.respondWith( caches.match(event.request).then(function(response) { if (response) { // retrieve from cache return response; } // fetch as normal return fetch(event.request); }) ); });
Vamos olhar mais de perto para o nosso service worker. Primeiro, estamos definindo uma variável cacheName
. Isso é usado para determinar se alguma alteração foi feita em nossos ativos em cache. Para este exemplo, usaremos um nome estático, o que significa que nossos ativos não serão alterados ou exigirão atualização.
self.addEventListener('install', function(e) { // declare which assets to cache }
O evento de install
é acionado durante a fase de instalação do service worker e será acionado apenas uma vez se o service worker já estiver instalado. Portanto, atualizar a página não acionará a fase de instalação novamente. Durante a fase de instalação, podemos declarar quais ativos serão armazenados em cache. Em nosso exemplo acima, estamos armazenando em cache um arquivo CSS, dois arquivos JavaScript, nosso arquivo de fontes, nosso modelo HTML offline e, claro, a raiz do aplicativo. self.skipWaiting()
força o service worker em espera a se tornar ativo.
Até agora, declaramos nosso service worker, mas antes de vê-lo entrar em vigor, precisamos referenciá-lo em nosso JavaScript. Em nosso aplicativo, registramos em main.js
// Register the service worker if available. if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw.js').then(function(reg) { console.log('Successfully registered service worker', reg); }).catch(function(err) { console.warn('Error whilst registering service worker', err); }); } window.addEventListener('online', function(e) { // Resync data with server. console.log("You are online"); Page.hideOfflineWarning(); Arrivals.loadData(); }, false); window.addEventListener('offline', function(e) { // Queue up events for server. console.log("You are offline"); Page.showOfflineWarning(); }, false); // Check if the user is connected. if (navigator.onLine) { Arrivals.loadData(); } else { // Show offline message Page.showOfflineWarning(); } // Set Knockout view model bindings. ko.applyBindings(Page.vm);
Também incluímos dois ouvintes de eventos para verificar se o estado da sessão mudou de online
para offline
ou vice-versa. Os manipuladores de eventos chamam as diferentes funções para recuperar os dados por meio de Arrivals.loadData()
e para habilitar ou desabilitar a mensagem offline por meio Page.showOfflineWarning
e Page.hideOfflineWarning
, respectivamente. Nosso aplicativo também verifica se o usuário está online no momento, usando navigator.onLine, e recupera os dados ou mostra o aviso offline de acordo. E na última linha de main.js
, aplicamos as ligações Knockout ao nosso View Model Page.vm
.
Se carregarmos nosso aplicativo pela primeira vez (com Chrome Developer Tools), não veremos nada de novo. No entanto, ao recarregar, veremos que vários recursos de rede foram recuperados do service worker. Este é o nosso shell de aplicação.
Teste off-line
Um usuário executando o aplicativo sem uma conexão com a Internet (supondo que já esteve na página) resultará simplesmente na exibição do shell do aplicativo e do aviso off-line - uma melhoria em relação ao t-rex do Chrome. Depois que o usuário estabelecer uma conexão de rede, desabilitamos o aviso e recuperamos os dados mais recentes.
O Guardian adota uma abordagem particularmente interessante quando usuários offline acessam seu site, fornecendo um jogo de palavras cruzadas:
Notificações via push
As notificações push permitem que os usuários optem por atualizações oportunas de aplicativos em que confiam, ajudando-os a se envolver novamente com os aplicativos. As notificações push na web permitem que você se envolva com seu público mesmo quando o navegador está fechado.
A API Push é suportada no navegador Chrome, Opera e Samsung e está em desenvolvimento no Firefox e Microsoft Edge. Infelizmente, não há indicação de que o recurso será implementado no Safari.
atuação
Uma das vitórias mais fáceis com os service workers é que podemos melhorar o desempenho com pouco ou nenhum esforço. Comparar nosso site consigo mesmo antes da implementação dos service workers, antes de recuperarmos mais de 200 KB no carregamento da página; que agora está reduzido para 13 KB. Em uma rede 3G normal, a página levaria 3,5 segundos para carregar; agora leva 500 milissegundos.
Essas melhorias de desempenho são drásticas porque o aplicativo em si é muito pequeno e tem funcionalidade limitada. No entanto, por meio do uso correto do cache, é possível melhorar significativamente o desempenho e o desempenho percebido, principalmente para usuários em locais com baixa conectividade.
Farol
A equipe do Google Chrome montou uma ferramenta para testar aplicativos da web progressivos. O Lighthouse é executado em Node.js ou como um plug-in do Chrome e também pode ser encontrado no GitHub.
Para executar um teste do Lighthouse, seu site precisa estar disponível online, o que significa que você não pode testar em localhost
.
Para começar, baixe o pacote npm:
npm install -g GoogleChrome/lighthouse
Uma vez instalado, execute o Chrome (versão 52 em diante):
npm explore -g lighthouse -- npm run chrome lighthouse https://incredibleweb.github.io/pwa-tutorial/
A saída da execução do Lighthouse ficará visível na linha de comando e classificará seu site de acordo com os recursos e propriedades do Progressive Web App que você implementou — por exemplo, se você estiver usando um arquivo manifest.json
ou se sua página estiver disponível offline .
Conclusão
Este artigo é apenas um aperitivo para aplicativos da web progressivos. Poderíamos fazer muito mais para criar a experiência semelhante a um aplicativo que os usuários procuram, seja suportando notificações push com a API Push, tornando o aplicativo reengajado ou usando IndexedDB e sincronização em segundo plano para melhorar a experiência offline.
Suporte entre navegadores
Ainda são os primeiros dias para aplicativos da Web progressivos, e o suporte entre navegadores ainda é limitado, especialmente no Safari e no Edge. No entanto, a Microsoft oferece suporte aberto a aplicativos da Web progressivos e deve implementar mais recursos até o final do ano.
- Trabalhadores de serviço e API de cache . Suportado no navegador Chrome, Firefox, Opera e Samsung. Em desenvolvimento no Microsoft Edge, deve estar disponível até o final de 2016. Em consideração para o Safari.
- Adicionar à tela inicial . Compatível com Chrome, Firefox, Opera, navegador Android e navegador da Samsung. A Microsoft parece indicar que os aplicativos da Web progressivos estarão disponíveis como listagens de loja. Ainda não há planos para o Safari.
- Empurre a API . Principalmente suportado no navegador Chrome, Firefox, Opera e Samsung. Em desenvolvimento no Microsoft Edge. Ainda não há planos para o Safari.
Se mais desenvolvedores aproveitarem os recursos oferecidos pelos aplicativos da Web progressivos - que são relativamente fáceis de implementar e fornecem recompensas imediatas - os usuários preferirão consumir esses aplicativos da Web em navegadores compatíveis, convencendo os outros fornecedores de navegadores a se adaptarem.
Código fonte
Todo o código-fonte deste tutorial está disponível em um repositório do Github e a demonstração está disponível no GitHub Pages.