Categorias
Web

PWAs em 2020

Todos os anos temos uma compilação dos últimas features referentes a PWA no Google I/O o congresso anual do Google para desenvolvedores. No último ano 2019 confesso que não fiquei muito empolgado com os lançamentos relacionados a PWA no Google I/O, mas analisando por um outro lado é o resultado de uma maturidade do conceito PWA.

Progressive web apps em si, já tem sua base, com Service Workers e Web manifest não temos muito o que mudar. Saber dos valores de criar apps rápidos, seguros e que cativem o usuário, por fim, temos a evolução das Web APIs esse é um ponto que ainda terá uma evolução, as Web APIs nos permitem implementar recursos antes só possíveis com aplicações nativas esse ponto irei falar mais pra frente.

Por conta da situação que estamos passando as principais conferências no mundo foram canceladas e o Google I/O também está incluso nessa lista então não teremos um evento principal com anúncio de novas features relacionadas a PWAs, mas esse ano acredito que teremos lançamentos menores com seguindo as versões do Chrome e numa previsão otimista teremos um Chrome Summit online.

Releases do Chrome

Com toda a situação com o covid-19 o calendário de lançamentos do Chrome foi alterado, tivemos na última semana a versão 81 sendo lançada com suporte em versão de teste a NFC e suporte total a badging api, mas Google anunciou que irá pular a versão 82 atrasando o anuncio de alguns novos recursos em 6 semanas, mas o Google irá trabalhar apenas em bugs e correções de segurança durante esse período e somente lançar a versão 83. Se a situação se normalizar eles voltarão ao ciclo de 6 semanas.

Projeto Fugu

Apresentação do Projeto Fugu no I/O 2019

Projeto Fugu, é um projeto que visa habilitar na plataforma web recursos que eram somente possíveis com apps nativos uma série de empresas estão envolvidas nesse processo. Mas Claro que os releases do Chrome influenciam na implementação de recursos PWA eles são responsáveis pelos browsers Android e recentemente é o motor por trás do Microsoft Edge.

Com a adoção do Edge ao Chromium aumentou a quantidade de desenvolvedores, assim dando um fôlego maior ao Projeto Fugu na implementação de novas web APIs. Não só apenas Google e Microsoft contribuem com o Projeto Fugu, mas temos empresas como Intel e Samsung como contribuidores.

Google compartilhar uma lista pública de quando as funcionalidades serão implementadas no Chrome, que você confere aqui: https://docs.google.com/spreadsheets/d/1de0ZYDOcafNXXwMcg4EZhT0346QM-QFvZfoD8ZffHeA/edit#gid=557099940

O legal dessa lista é atualizada a cada release do chrome, outro ponto importante você também pode contribuir com sugestões de features: https://developers.google.com/web/updates/capabilities

Recursos para PWAs já lançados em 2020(Abril)

  • Web Share API target
  • Contact Picker API
  • Periodic Background Sync
  • Web OTP API – Screen Lock
  • Clipboard para imagens
  • Launch keyboard Map API

Recursos da Web API previstas para 2020

  • Web NFC
  • Content Index API
  • Screen enumeration API
  • File Handling API
  • getInstalledRelatedApps
  • Native File system API
  • WakeLock API – Screen Lock
  • Shape detection API

iOS

Quase contra-mão o Webkit trabalha de forma isolada, tratando de recursos que a Apple acha prioridade para o safari, por exemplo, na última versão do safari 13.1 tivemos a inclusão de recursos como:

  • Async Clipboard API
  • Picture-in-Picture API
  • Remote Playback API

Muitas empresas já viram o benefício de se investir em PWAs, mas a Apple evita de mencionar o termo PWA por conta de ser um termo vindo do Google para definir o conceito. Segundo acredito no ponto de vista relacionado ao business atual da empresa que a Apple Store, como ela irá encaixar PWAs com apps nativos.

Particularmente acredito que a Apple continuará implementando novos recursos da Web API quando é de seu interesse e a tentará uma implementação da formula diferente da apresentada pelo Google. Mas 2020 ainda não será o ano que teremos full suporte pela Apple. Para saber mais sobre PWA confira a página do curso de Progressive web Apps.

Categorias
AMP Accelerated mobile pages

Convertendo uma aplicação AMP em uma PWA

Agora chegou o momento de conectar os meus dois assuntos favoritos, Progressive Web Apps(PWA) e AMP. Se você ainda não sabe o que é uma PWA podemos definir conjunto de tecnologias que nos permitem criar aplicações web com recursos que antes só possíveis em aplicações nativas se quiser saber mais sobre PWA tenho um curso dedicado em meu canal.

As PWA possui uma série de diferentes API que podem ser aplicadas a diferentes soluções, mas para o caso de nossa aplicação AMP vamos abordar dois items:

  1. Tonar nossa aplicação instalável
  2. Adicionar suporte a versão offline.

Criando um web app manifest para nossa PWA

Primeiro passo vamos criar um web app manifest arquivo json que permite que dispositivos que reconheçam que nossa aplicação pode se instalável. Para isso vamos adicionar em nosso projeto um arquivo chamado manifest.json e nele vamos adicionar o seguinte código:

{ "short_name": "Caipirinha", "name": "How to prepare the best caipirinha", "icons": [ { "src": "./images/capi_icon_192.png", "type": "image/png", "sizes": "192x192" }, { "src": "./images/capi_icon_512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": "/index.html", "background_color": "#00d1c3", "display": "standalone", "scope": "/", "theme_color": "#00d1c3" }
Code language: JSON / JSON with Comments (json)

O código acima descreve as informações que iremos exibir para o usuário quando ele instalar nossa aplicação, que são elas:

  • short_name: o nome que sera exibido na tela inicial do nosso usuário.
  • icons: com uma lista de ícones que iremos exibir em diferentes plataformas.
  • start_url: o endereço nossa aplicação irá carregar quando acessada através do ícone na tela inicial do usuário.
  • background_color: a cor do background da splash screen do nosso app.
  • display: o modo em que nossa PWA será exibida
  • theme_color: as cor do tema da nossa aplicação, por exemplo, barra de controle.

Depois que criarmos nosso arquivo manifest.json temos adicionar uma referência dentro da tag head de nosso index.html ao arquivo de configuração, como podemos ver no código abaixo:

<link rel="manifest" href="/manifest.json">
Code language: HTML, XML (xml)

Adicionando suporte a versão off-line

Agora que já definimos como nosso app será instalado é hora de adicionar a versão off-line de nossa aplicação para isso vamos trabalhar com service workers.

Service Worker um script que é executando por nosso browser em background, separado de nosso DOM onde funciona como proxy acompanhando as requisições no nosso app.

Caso queira saber mais sobre o assunto confira o post sobre service worker.

Mas com esse recurso conseguimos controlar quando nosso app faz requisições a rede e definir estratégias de cache e suporte a versão offline utilizando em conjunto com a cache API.

Atualmente existem bibliotecas que facilitam a implementação desses recursos, por exemplo, Workbox onde também tenho um post mais detalhado sobre o assunto aqui.

Mas AMP vai um passo além do Workbox e abstrai uma série de passos na configuração de um service worker para implantação de estratégias de cache e versão off-line. Isso tudo utilizando AMP Service Worker ele armazena automaticamente os scripts e os documentos de nossa aplicação.

Como criar um service worker com AMP?

Passo 1 – criar o arquivo que irá importar o AMP Service Worker e incializa-lo, para isso vamos criar um arquivo JavaScript chamado sw.js e adicionar os seguinte código:

importScripts('https://cdn.ampproject.org/sw/amp-sw.js'); AMP_SW.init();
Code language: JavaScript (javascript)

Passo 2 – Incluímos o script que irá instalar o AMP Service Worker próximos as scripts que você já carrega em sua aplicação AMP.

<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>
Code language: HTML, XML (xml)

Passo 3 – Adicionamos dentro do corpo da nossa página HTML o componente amp-install-servieworker:

<amp-install-serviceworker src="/sw.js" data-iframe-src="install-sw.html" layout="nodisplay"> </amp-install-serviceworker>
Code language: HTML, XML (xml)

O componente amp-install-serviceworker irá registrar um service worker definido na propriedade src, dois pontos importante sobre o service worker:

  • Precisa estar na pasta raiz da aplicação
  • Precisa ser servido via HTTPS.

A segunda propriedade é o data-iframe-src é um atributo opicional que será responsável por verificar se o browser possui suporte a service worker e instalar-lo em caso possua.

Passo 4 – o último passo vamos criar o arquivo definida pela propriedade data-iframe-src:

<!doctype html> <title>installing service worker</title> <script type='text/javascript'> if('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw.js'); }; </script>
Code language: HTML, XML (xml)

Com esse 4 passos temos nossa primeira versao offline, o AMP Service Worker nos permite definir estratégias de cache para arquivos especificos. Caso queira saber mais sobre estrategias de cache eu escrevi um post aqui.

Vou criar mais um post de como modificar estratégias de cache e exibir uma página específica parar quando os usuários estiverem off-line.

Categorias
JavaScript PWA - Progressive web apps

Introdução a Workbox

Workbox é um conjunto de bibliotecas e módulos do node que simplifica o processo de cache de assets em nossa aplicação, assim agilizando o nosso trabalho na criação de uma Progressive Web Apps. Este tutorial será baseado na versão 4 da biblioteca. Workbox trabalha dois conceitos importantes sobre PWAs:

Performance: não espere por todos os arquivos de sua aplicação virem da internet, crie estratégias de cache para servir arquivos do armazenados em seu device.

Resiliência: Conexões móveis ou em regiões com fraca infra estrutura podem afetar a experiência do usuário, habilite sua aplicação administrar situações que a conectividade é limitada

Porquê Workbox?

Workbox é uma biblioteca reune as melhores práticas e toda a complexidade de trabalhar com service workers com por exemplo:

  • Precaching
  • Runtime caching
  • Estratégias de cache
  • Requisição de rotas
  • Background Sync
  • Ajuda no debug da aplicação

Opções de utilização

Para trabalhar com Workbox temos as seguintes alternativas:

  • Workbox CLI
  • node.js
  • webpack plugin

Lembrando que essas alternativas são independentes você deve escolher somente uma, então escolha a alternativa que melhor se adequa ao seu caso.

Workbox CLI

CLI nada mais é que uma acrônimo para Command line Interface(Interface de linha de comando) tem como objetivo trazer um grupo de comandos no terminal para possibilitar realizar uma terminada ação, seja criar novos projetos ou administrar recursos existentes. Mas tendo como foco principal reduzir o trabalho e a complexidade ao item aplicado.

Primeiro passo quando trabalhamos com um CLI é fazer sua instalação workbox-cli depende do node.js para funcionar, antes de rodar qualquer comando para o CLI precisamos instalar a versão mais recente do node.js, após a instalação do node rodamos os seguinte comando em nosso terminal:

npm install workbox-cli --global
Code language: PHP (php)

Se tudo ocorrer bem seremos capazes de testar com o seguinte comando:

workbox --help

Se o terminal reconheceu o comando workbox sinaliza que a instalação foi bem sucedida, agora é hora de utilizar o comando:

workbox wizard

Ele irá nos dar um passo-a-passo para a configuração de nosso projeto com as seguintes perguntas:

  1. Qual é a pasta do nosso app onde se encontra a pasta em que iremos exportar nossa aplicação?
  2. Quais os tipos de arquivos em que gostaríamos de realizar o preache?
    1. svg, jpg, png, html, css, js, json
  3. Onde preferimos salvar o nosso service worker?
  4. Onde gostariamos de salvar nosso arquivo de configuração

Como podemos ver na imagem abaixo, no meu caso estou rodando a versão 4.3.1 do Workbox, versões anteriores ou futuras esse passos podem mudar um pouco mas o core sempre será quais arquivos queremos fazer o cache e qual será o nosso service worker.

Por fim ele exibe o comando necessário para gerar nosso service worker:

workbox generateSW workbox-config.js
Code language: CSS (css)

Quando rodamos esse comando ele irá ler o arquivo de configuração gerado pelo wizard:

module.exports = { "globDirectory": "dist/", "globPatterns": [ "**/*.{svg,jpg,png,html,css,js,json}" ], "swDest": "dist/sw.js" };
Code language: JavaScript (javascript)

Varrer e criar um cache para todos os arquivos com as extensões especificadas anteriormente. Você terá um retorno parecido com a mensagem a seguir.

The service worker was written to dist/sw.js 22 files will be precached, totalling 84.2 kB.

Esse comando será necessário toda a vezes em que alteramos os arquivos dentro de nossa aplicação, para agilizar esse processo podemos fazer uma integração com gulp ou webpack.

Gulp + workbox

Como mencionamos anteriormente podemos agilizar o processo de criação de service worker utilizando ferramentas de automatização de tarefas, basicamente vamos assistir as mudanças realizadas em nosso projeto quando algum arquivo for alterado o Gulp será responsável por gerar o nosso service worker

Instalação:

npm install workbox-build --save-dev

Exemplo

const workboxBuild = require('workbox-build'); // NOTE: Esse comando deve ser executado depois que os assets forem gerados const buildSW = () => { // assim retornamos generateSW que retorna uma Promisse return workboxBuild.generateSW({ globDirectory: 'build', globPatterns: [ '**\/*.{html,json,js,css}', ], swDest: 'build/sw.js', }); }
Code language: JavaScript (javascript)

webpack + workbox

Para webpack também temos um plugin especifico para trabalhar com workbox ele tem suporte precaching e runtime caching. Caso ainda não conheça webpack tenho um post de introdução aqui.

Para instalação do plugin na pasta de nosso projeto onde ficar o nosso arquico package.json executamos o seguinte comando em nosso terminal:

npm install workbox-webpack-plugin --save-dev

Depois de instalado precisamos configurar o nosso webpack, dentro do arquivo de configuração do webpack adicionamos o Plugin para workbox:

// dentro do webpack.config.js: const WorkboxPlugin = require('workbox-webpack-plugin'); module.exports = {   // Outras configurações...   plugins: [     // Outros plugins...     new WorkboxPlugin.GenerateSW()   ] };
Code language: JavaScript (javascript)

Após tudo configurado precisamos registrar nosso service worker:

<script> // verificamos se o browser suporta service worker if ('serviceWorker' in navigator) { // usamos o evento load para registrar nosso service worker window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js'); }); } </script>
Code language: HTML, XML (xml)

Caso queira saber mais sobre service worker tenho um post de introdução a service worker também estou rodando um curso gratuito sobre PWA em meu canal no youtube. Qualquer dúvida só deixar um comentário e até o próximo post.

Categorias
JavaScript PWA - Progressive web apps

Estratégias de cache para PWAs

Continuando a série sobre PWA tivemos uma introdução sobre Service Worker. Vimos que o service worker permite o suporte a aplicações offline mas também podemos controlar as requisições feitas pelo o navegador isso nos dá a habilidade de pensar em diferentes estratégias de cache para a nossa aplicação e esse será o tema do nosso post.

Desenvolvendo uma aplicação web alguns dados dentro da nossa solução tem uma periodicidade maior ou menor de atualização, por exemplo a logo de nosso site é tipo de dado que pode passar anos sem alteração, já no caso de uma sessão de últimas notícias podem ter uma periodicidade de minutos. Com esse dois exemplo podemos ver situações distintas, assim com a partir desses exemplos vamos abordar as estratégias mais comum de cache.

Cache only

Nesse caso todos as requisições serão direcionadas para o cache.

  1. O service worker recebe a requisição
  2. Consulta se o conteúdo se encontra no cache
  3. Retorna o conteúdo requisitado para o usuário

Se não encontrado no cache a requisição irá falhar. Mas esse pattern assume que os conteúdos serão armazenados no cache durante a instalação do service worker.

Casos recomendados: esse padrão é ideal para assets que raramente são atualizados, por exemplo, logos, ícones de redes sociais e estilos. Mas isso não significa que eles nunca serão alterados, esse controle será feito pela versão do seu service worker.

Código:

self.addEventListener('fetch', event => { // todos os arquivos serão servidos pelo cache event.respondWith(caches.match(event.request)); })
Code language: PHP (php)

Cache first

Parecida com a estratégia anterior, mas com uma diferença, caso não ache o arquivo no cache ele irá realizar uma requisição na rede como podemos ver no código a seguir:

self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request).then(response => { return response || fetch(event.request); }) ); });
Code language: PHP (php)

Network only

  1. O service worker irá analisar a requisição
  2. Irá requisitar na rede os arquivos requisitados
  3. O conteúdo requisitado é enviado para o usuário

Neste caso se a requisição a rede falhar não retornará nada.

Casos recomendados: Esse útil para requisições exemplo pings no servidor para análise de tráfego ou estatísticas.

Network first

Nessa situação primeiramente fazemos o request para a rede, caso a requisição falhe procuramos o arquivo no cache. Problema nessa requisição que perdemos muito tempo esperando um retorno de uma falha na requisição.

Casos recomendados Trabalhamos com essa estratégia quando temos a prioridade dos dados recentes. Ideal para áreas que tem atualizações constantes.

Código:

self.addEventListener('fetch', event => { event.respondWith( fetch(event.request).catch(() => { return caches.match(event.request); }) ); });
Code language: PHP (php)

Cache, then network

Essa estratégia carrega os dados imediatamente do cache enquanto verifica na rede se o conteúdo sofre alguma alteração. No momento que receber o retorno das informações da redes, o conteúdo será verificado caso precisa será atualizado.

Temos o benéfico de exibir o conteúdo imediatamente mas sempre fazemos duas requisições precisando ou não da informação. Por que nesse caso só sabemos se o conteúdo foi alterado se requisitamos a informação na rede, se nada mudou, fazemos uma requisição em vão. Outro desafio desse padrão é desenvolver uma interface que não pareça estranho a atualização do conteúdo.

Casos recomendados: Quando priorizamos a performance mas temos uma frequência constante de updates do conteúdo, aplicações com timeline de e game leader board.

Código (sw.js)

self.addEventListener('fetch', event => { event.respondWith( caches.open('mysite-dynamic').then(cache => { return fetch(event.request).then(response => { cache.put(event.request, response.clone()); return response; }) }) ); });
Code language: PHP (php)

Código(main.js)

let networkDataReceived = false; startSpinner() const networkUpdate = fetch('/data.json').then(response => { if(!response) throw Error('no data'); return response.json(); }).then(data => { if(!networkDataReceived); updatePage(data); }).catch(() => { return networkUpdate; }).catch(showErrorMessage).then(stopSpinner);
Code language: JavaScript (javascript)

Stale while revalidate

Utilizamos essa técnica quando não temos prioridade do conteúdo mais recente mas queremos fazer atualizações periódicas. Essa estratégia é parecida com a cache first, mas com uma diferença os dados mais recentes não serão atualizados imediatamente, o cache será atualizado e a informação será exibida quando o usuário recarregar a página.

Casos recomendados: Logos, imagens do perfil do usuário.

Código

self.addEventListener('fetch', event => { event.respondWith( caches.open('mysite-dynamic').then(cache => { return cache.match(event.request).then(response => { const fetchPromise = fetch(event.request) .then(networkResponse => { cache.put(event.request, networkResponse.clone()); return networkResponse; }); return response || fetchPromise; }) }) ); });
Code language: JavaScript (javascript)

Generic fallback

Quando o usuário usuário não achar o conteúdo requisitado na rede ou no cache retomamos um conteúdo genérico. Por exemplo, quando não conseguimos encontrar uma imagem específica como um avatar, para não prejudicar nossa interface retornamos um avatar genérico, por exemplo, uma ilustração. Esse padrão pode ser combinado com as estratégias anteriores.

Código

self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request).then(response => { return response || fetch(event.request); }).catch(() => { return caches.match('/offline.html'); }) ); });
Code language: PHP (php)

Esse são as principais estratégias de cache, além delas podemos combinar estratégias de cache, mas para esse post vamos abordar os mais populares nos próximos posts iremos abordar uma ferramenta que irá facilitar nossa vida: Workbox, mas isso é assunto para um novo post.

Caso queira saber mais sobre Progressive web apps confira o curso que gravei em meu canal do youtube. Caso tenha alguma dúvida deixe um comentário e até o próximo post.

Categorias
JavaScript Tutoriais Web

Introdução a Service Worker

Nesse posts vamos falar sobre service worker é um dos recursos chaves dentro das Progressive web Apps vamos entender como ele funciona e o seu ciclo de vida. Para quem ainda não viu, estou gravando uma série sobre PWA em meu canal do youtube, até o momento tenho mais de 12 aulas gravadas também estou adicionando conteúdo complementar no blog.

O que é um service worker?

Nada mais é que o coração das PWAs, não é nenhuma tecnologia especial e sim um arquivo JavaScript que roda em segundo plano em nossa aplicação o que isso significa? Ele não é executado na thread principal da aplicação junto com execução do HTML e CSS, ele não tem acesso ao DOM por exemplo. Isso abre uma porta para recursos rodarem independente do browser por exemplo push notifications e execução de código em segundo plano. Outro recurso extremamente importante é o suporte a experiência offline, dando ao usuários controle sobre a aplicação independente da conectividade.

Pontos importantes sobre Service Worker

  • Precisa estar rodando em HTTPS
  • Ele é uma JavaScript Worker, ele não tem acesso ao DOM
  • Permite controlar como serão feitos os requests a rede
  • Faz o uso intensivo de promises
  • Precisa ser instalado por outro JavaScript
  • Possui um escopo e esse escopo é baseado onde o arquivo do service worker se encontra.

Ciclo de vida

Quando registrado por outro JavaScript, o service worker passa a ter um ciclo de vida independente do conteúdo web em que foi registrado. Ele será movido para outra Thread como mencionamos anteriormente, quando registrado ele irá chamar o evento Install event, se durante o processo de instalação ocorrer um erro na transferência de algum arquivo o processo de instalação irá falhar. mas se tudo ocorrer bem ele irá disparar um segundo evento Active event.

Depois que o Active event é disparado o service worker está pronto para disparar funções em segundo plano e acompanhar os dados que estão sendo transferidos entre aplicação e a rede. Neste momento conseguimos definir estratégia de cache e suporte offline. A imagem abaixo resume os passos que acabamos de comentar.

Ciclo de vida de um service worker

Na imagem acima temos uma definição completa do ciclo de vida de um service worker, com adição a um ponto importante quando adicionamos uma nova versão ela fica em estado de espera, ela só será instalada quando todas as instâncias da nossa aplicação forem finalizadas.

Registrando um Service worker

if ('serviceWorker' in navigator) { window.addEventListener('load', function() { navigator.serviceWorker.register('/sw.js').then(function(registration) { // service worker registrado com sucesso console.log('ServiceWorker registrado no seguinte escopo: ', registration.scope); }, function(err) { // falha no registro:( console.log('Falha no registro do ServiceWorker: ', err); }); }); }
Code language: JavaScript (javascript)

O código acima será implementado pelo o JavaScript responsável por registrar o nosso Service Worker, na primeira linha verificamos se o nosso browser possui suporte a tecnologia, caso tenha, o registramos com a função com navigator.serviceWorker.register e através de uma promise verificamos se o service worker foi instalado com sucesso. E dentro do nosso sw.js podemos adicionar listener para acompanhar o seu ciclo de vida:

self.addEventListener('install', function(event) { // acompanha a instalação do service worker }); self.addEventListener('activate', function(event) { // acompanha quando o service worker está ativo }); self.addEventListener('fetch', function(event) { // acompanha quando a aplicação faz transferência de arquivos });
Code language: PHP (php)

Os listeners serão importante para criar um cache, acompanha mensagens e definir estratégias de cache. Para esse post, vamos fechando por aqui, se ainda não segue o meu canal no Youtube, lá estou sempre postando conteúdo sobre PWA: https://www.youtube.com/user/fdangellys

Veja os posts sobre PWA aqui

Veja também o mini curso sobre PWA