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).
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.
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.