Recentemente, a Wavy  lançou um aplicativo de notícias para o SBT, o SBT News.  Ele pode ser acessado em qualquer device clicando aqui, e sendo um progressive web app, ele possui algumas features que antes só eram disponíveis em aplicativos nativos, como notificações push e acesso offline.

Porque não um aplicativo nativo?

Um dos maiores desafios para construir um aplicativo mobile é a barreira de download, não são muitos os usuários que estão dispostos a gastar o espaço precioso com seu aplicativo ou até mesmo a gastar o tempo que leva para baixa-lo. Isso é uma grande vantagem de websites sobre aplicativos nativos, para os usuários, abrir um site é tão difícil quanto clicar em um link.

Mas o que torna um Progressive Web App (PWA para os íntimos) especial?

Bom, realmente não é muita coisa. Um PWA é basicamente uma aplicação web padrão, mas com algumas features extras para smartphones (apesar de terem benefícios para aplicações desktop também).

Captura de Tela 2018-08-29 às 17.17.12.png

As principais features são:

  • Notificações push
  • Abrir o seu app mesmo sem conexão
  • Cache offline automático dos assets da sua aplicação (muito bom para usuários com conexões ruins)
  • Um ícone customizado para a home do usuário e uma splash screen para abrir o seu app.

Essas features são com certeza bem vindas, mas na essência, todo progressive web app é simplesmente uma aplicação web com dois arquivos extras: o service worker, e o manifest.json.

O Service Worker

Esse é o arquivo mais importante de um PWA, ele é responsável pelo cache do seu website e dos seus assets. Ele é só um arquivo javascript que vai ser responsável por todo o trabalho pesado para você.

Você pode encontrar diferente estratégias de caching e exemplos do service worker aqui.


const isLocalhost = Boolean(
 window.location.hostname === ‘localhost’ ||
   window.location.hostname === ‘[::1]’ ||
   window.location.hostname.match(
     /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
   )
);

export default function register() {
 if (process.env.NODE_ENV === ‘production’ && ‘serviceWorker’ in navigator) {
   const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
   if (publicUrl.origin !== window.location.origin) {
     return;
   }

   window.addEventListener(‘load’, () => {
     const swUrl = ${process.env.PUBLIC_URL}/service-worker.js;

     if (isLocalhost) {
       checkValidServiceWorker(swUrl);
       navigator.serviceWorker.ready.then(() => {
         console.log(
           ‘This web app is being served cache-first by a service ‘ +
             ‘worker. To learn more, visit https://goo.gl/SC7cgQ’
         );
       });
     } else {
       registerValidSW(swUrl);
     }
   });
 }
}

function registerValidSW(swUrl) {
 navigator.serviceWorker
   .register(swUrl)
   .then(registration => {
     registration.onupdatefound = () => {
       const installingWorker = registration.installing;
       installingWorker.onstatechange = () => {
         if (installingWorker.state === ‘installed’) {
           if (navigator.serviceWorker.controller) {
             console.log(‘New content is available; please refresh.’);
           } else {
             console.log(‘Content is cached for offline use.’);
           }
         }
       };
     };
   })
   .catch(error => {
     console.error(‘Error during service worker registration:’, error);
   });
}

function checkValidServiceWorker(swUrl) {
 fetch(swUrl)
   .then(response => {
     if (
       response.status === 404 ||
       response.headers.get(‘content-type’).indexOf(‘javascript’) === -1
     ) {
       navigator.serviceWorker.ready.then(registration => {
         registration.unregister().then(() => {
           window.location.reload();
         });
       });
     } else {
       registerValidSW(swUrl);
     }
   })
   .catch(() => {
     console.log(
       ‘No internet connection found. App is running in offline mode.’
     );
   });
}

export function unregister() {
 if (‘serviceWorker’ in navigator) {
   navigator.serviceWorker.ready.then(registration => {
     registration.unregister();
   });
 }
}

O manifest.json

Ele é responsável por como o seu app vai ficar na home do seu usuário, qual a imagem do ícone e do splash, a cor da barra de notificações, etc. Basicamente toda a configuração visual do seu PWA acontece neste arquivo.

É simplesmente um arquivo de configuração, nenhuma mágica.

{
 “short_name”: “My PWA”,
 “name”: “My Progressive Web App”,
 “icons”: [
   {
     “src”: “favicon.ico”,
     “sizes”: “64×64 32×32 24×24 16×16”,
     “type”: “image/x-icon”
   }
 ],
 “start_url”: “./index.html”,
 “display”: “standalone”,
 “theme_color”: “#000000”,
 “background_color”: “#ffffff”
}

 

create-react-app

Uma boa notícia para quem usa o create-react-app, assim que o bootstrap da aplicação é feito, ele já gera um service worker funcionando e também uma base de um manifest.json, então é extremamente fácil começar a brincar com progressive web apps e foi assim que a configuração inicial do SBT News foi feita 😉

Performance

A característica mais importante do seu PWA é que ele precisa ser rápido, realmente rápido. E faz sentido, se a sua aplicação for lenta e travada, demorando décadas para abrir, qual o sentido de fazer uma PWA? O objetivo é ter uma experiência o mais próxima possível de uma aplicação nativa.

Para isso existem algumas ferramentas muito úteis para tornar o seu site mais rápido, primeiro de tudo, uma forma de medir performance. A ferramenta de audit do Google Chrome é interessante para analisar os tempos de renderização e tempos para primeira interação, e o melhor de tudo, já está disponível no seu browser! É só acessar a aba de audits dentro do devtools do chrome. E o seu objetivo é tornar a pontuação do seu site o mais alta possível.

Captura de Tela 2018-08-29 às 17.18.40.png

Além disso, o audits faz análises de acessibilidade, boas práticas, e de quantos itens da checklist de progressive web apps você cumpre!

O que mais impacta na performance é utilizar gzip nos seus assets, isso pode ser ativado tanto no seu servidor no caso de server-side rendering ou na sua cdn no caso de um site estático, como é o caso do SBT Notícias. Apenas por habilitar a compressão, já é possível conseguir notar grandes melhorias na performance da aplicação.

Outra estratégia simples para melhorar o seu score, é fazer code-splitting por rotas, você pode tanto usar o webpack code splitting ou usar uma biblioteca como react-loadable. Isso vai ajudar a diminuir o tamanho do bundle inicial, e consequentemente o tempo de loading inicial.

CSS Componentizado

Outro ponto que não é tão relacionado com uma PWA, mas que teve bastante importância na forma que o SBT News foi desenvolvido, é como lidamos com CSS. Em react, temos algumas opções em relação à CSS como escrever CSS global da forma que sempre fizemos ou usar estilo com escopo de componentes. Nós seguimos com a segunda alternativa, já que ela permite uma quantidade bem menor de efeitos colaterais quando editando o CSS, tornando o trabalho de lidar com todos os estilos muito mais fácil a medida que a  aplicação cresce.

Mas para fazer isso com uma simples aplicação create-react-app você tem duas opções: ou nomeando as classes CSS com um namespace específico por componente na mão, ou usar a prop style nos componentes diretamente. Essas opções não são ideias, pois nomear manualmente as classes dá trabalho e não é um processo perfeito, e no caso da prop style, perdemos features importantes como media queries e pseudo selectors. Então acabamos seguindo com o styled-components, que tornou todo esse processo muito mais simples, podendo escrever basicamente CSS diretamente nos componentes.

E o melhor de tudo é que com o styled-components temos acesso à todas as features avançadas do CSS, como like pseudo, media-selectors e transições.

Além disso, é possível encontrar também alguns pequenos benefícios para a performance, já que como cada componente tem apenas o CSS que precisa para se desenhar, isso aliado com code splitting permite separar o seu CSS em várias partes. Então para cada página, será carregado apenas o CSS necessário para desenhar seus componentes, evitando assim que o navegador precise analisar blocos monolíticos de estilo que nem estão sendo utilizados na tela exibida.

Posted by:João Gonçalves

<span class="lt-line-clamp__line">Working with development of progressive web apps in React.js, RESTful and GraphQL apis with</span> <span class="lt-line-clamp__line lt-line-clamp__line--last">node.js and spring framework.</span>

Deixe seu comentário