menu

SHARKLABS

Vue.js + PWA + Workbox: Aplicativo Off-line com Background-Sync

/
/
Vue.js + PWA + Workbox: Aplicativo Off-line com Background-Sync
bookmark Vue.js, PWA access_time

Progressive Web App

Os Progressive Web Apps é uma iniciativa que tem como objetivo trazer uma experiência off-line para as páginas e aplicativos Web. Essa experiência off-line é inspirada em aplicativos desktop e mobile que não precisam Internet para funcionar.

Inicialmente isso pode parecer meio confuso e contraditório, uma vez que a principal premissa da Web é estar on-line. Mas nos últimos anos, novas funcionalidades foram adicionadas aos principais navegadores e essa experiência off-line já uma realidade.

Vou explicar como isso é possível e como framework Vue.js pode te ajudar.

Service Worker: Entenda o básico

Para começar vamos falar sobre os Service Workers, que é uma camada entre o front-end e o back-end. Tecnicamente chamamos de isso proxy, mas é possível fazer uma analogia com uma "ponte" que faz a ligação entre duas margens. É nessa "ponte" que será decidido quem pode ou não passar para o outro lado.

Ou seja, toda requisição HTTP feita pelo front-end passará pelo Service Worker que decide se envia a requisição para o back-end ou se busca uma resposta no cache. Não vou me aprofundar sobre isso, mas se você quer saber mais sobre a API dos Service Workers, recomendo que veja este excelente tutorial do Google Developers.

Service Worker: Flexibilidade VS Complexidade

O fato do Service Worker ser um proxy o torna muito flexível, uma vez que você tem total liberdade para programar o tratamento das requisições. Por outro lado, em situações simples isso pode trazer uma complexidade desnecessária.

Por exemplo: Para que seu PWA funcione off-line, na primeira requisição HTTP você precisa fazer o download dos Assets e armazená-los em cache. Nas requisições HTTP seguintes, você entrega o conteúdo que está cache e somente em caso de alterações no conteúdo dos Assets você faz um novo download.

É muito comum ver este tipo de comportamento em um PWA e por isso logo surgiram algumas boilerplates que faziam isso. Mas o problema estava na manutenção, principalmente para atualizar as boilerplates que foram modificadas.

Workbox: Service Worker de um jeito fácil

Com a ideia de padronizar a implementação do Service Worker e facilitar a programação de alguns comportamentos é que surgiu o Workbox. Desenvolvido pelo Google, o Workbox é uma biblioteca que ajuda na implementação do Service Worker.

Basicamente Workbox possui um núcleo (chamado de "workbox.core") e vários módulos que são carregados conforme as funcionalidades são requeridas. É possível fazer várias coisas com Workbox, mas por enquanto vou explicar somente as principais funcionalidades.

Lembrando que o Workbox sempre deve ser importado dentro do Service Worker. Clique aqui para ver como importar o Workbox.

Workbox Precaching

O Precaching é uma maneira de fazer o download dos Assets (estou de falando de arquivos HTML, JavaScript, CSS e imagens) no momento da instalação do Service Worker. Ou seja, quando o Service Worker precisar destes arquivos eles já estarão em cache.

Basicamente basta passar uma listagem de arquivos para a função workbox.precaching.precacheAndRoute e automaticamente será feito download e cache dos arquivos. Para mais detalhes clique aqui para ler a documentação completa do Precaching.

Workbox Routing e Strategies

O Workbox Routing é um módulo e o Workbox Strategies é outro módulo, porém eles trabalham juntos. O Routing tem o objetivo de interceptar as requisições HTTP do seu aplicativo e o Strategies define o comportamento dessa interceptação.

A princípio o módulo Strategies tem 5 comportamentos padrões. O primeiro deles é Stale-While-Revalidate que responde a requisição com o conteúdo armazenado em cache (uma resposta muito rápida), mas em seguida verifica (em background) se o conteúdo sofreu alteração. Caso haja alguma modificação no conteúdo, o cache é atualizado.

O segundo comportamento do Strategies é o Cache First, que vai responder a requisição com o conteúdo em cache. Caso o conteúdo não exista no cache, a requisição será enviada ao back-end.

O terceiro comportamento é o Network First, que sempre vai enviar a requisição ao back-end e a sua resposta será enviada ao front-end. Caso haja algum problema de rede a requisição é verificada no cache.

O quarto comportamento é o Network Only, que somente envia a requisição ao back-end e retorna a resposta ao front-end. Neste caso o cache nunca é verificado.

O quinto e último comportamento é o Cache Only, que sempre vai responder a requisição com o conteúdo em cache.

Caso seja necessário, é possível utilizar Routing sem o Strategies, mas aqui é necessário passar uma função com um comportamento personalizado.

Segue alguns exemplos:

// Using Strategie: StaleWhileRevalidate
workbox.routing.registerRoute(
  ({ url }) => url.pathname == '/my-url-test/1',
  new workbox.strategies.StaleWhileRevalidate()
);

// Using Custom Strategie
workbox.routing.registerRoute(
  ({ url }) => url.pathname == '/my-url-test/2',
  ({ event }) => fetch(event.request).then((response) => response.text())
);

Workbox Background Sync

Para ter a mesma experiência de um aplicativo mobile não basta apenas mostrar as telas em modo offline, é preciso interagir com o usuário e enviar requisições para back-end quando a conexão com a Internet se restabelecer.

Pensando nisso, em 2015 foi adicionado no Chrome uma API para sincronização em segundo plano. A ideia aqui é tentar executar uma Promise e caso ela falhe, o navegador futuramente tentará executar essa Promise novamente. Tem um excelente artigo do Google Developers que mostra como isso funciona.

É claro que o Workbox implementa isso e de um jeito bem mais fácil. Basta você interceptar uma rota com o Routing e utilizar a Strategie NetworkOnly com o plugin workbox.backgroundSync.Plugin. Segue um exemplo:

workbox.routing.registerRoute(
  ({ url }) => url.pathname == '/person',
  new workbox.strategies.NetworkOnly({
    plugins: [new workbox.backgroundSync.Plugin('myQueueName')],
  }),
  'POST'
);

Feito isso, quando a conexão se restabelecer a requisição será enviada ao back-end, mesmo que seu navegador esteja fechado. Para saber mais sobre o Workbox Background Sync este é o link da documentação oficial.

Periodic Background Sync

Ainda em relação as sincronizações em segundo plano, existe uma opção para executar uma sincronização agendada em segundo plano. Ela ainda está em fase de testes, mas logo deve ser liberada para todos os usuários do Chrome.

Ou seja, você pode definir um processo em segundo plano que será executado em intervalos personalizados (que você pode definir). Existe um excelente artigo escrito por Jeff PosnickJoe Medley que conta mais detalhes sobre este recurso, caso você tenha interesse este é o link.

No momento que escrevo este artigo, o Workbox ainda não possui uma implementação para esta funcionalidade, mas logo deve surgir alguma coisa.

Vue CLI: Opção PWA Suport

Quando você vai criar um projeto com o Vue CLI, existe uma opção se chama: "Progressive Web App (PWA) Support". Se você selecionar esta opção, serão adicionadas  ao seu projeto os pacotes "@vue/cli-plugin-pwa" e "register-service-worker".

Quando você executar o comando npm run build o pacote @vue/cli-plugin-pwa criará um arquivo Service Worker que importa o Workbox e já faz o Precaching de todos os seus Assets (arquivos HTML, JavaScript, CSS e imagens).

Service Worker: Como gerar automaticamente?

Você deve estar se perguntando, como este arquivo é gerado automaticamente? É que existe um plugin para Webpack que gera o Service Worker com o Workbox.

Por padrão este plugin somente configura o Service Worker para fazer Precaching, mas caso você queira outras opções, como Routing ou Background Sync, você precisa configurar isso no arquivo webpack.config.js .

Neste link você encontra todas opções para personalizar a geração do seu Service Worker.

Há um porém, nos projetos criados com o Vue CLI não existe o arquivo webpack.config.js , mas você pode fazer todas as configurações com o arquivo vue.config.js. Neste link você encontra instruções de como configurar o seu Service Worker por meio do vue.config.js.

Segue também um exemplo de como você pode configurar o vue.config.js:

module.exports = {
  pwa: {
    name: 'My App',
    themeColor: '#0c0e8a',
    manifestOptions: {
      background_color: '#0c0e8a',
      icons: [
        { src: './img/icons/icon-512x512.png', sizes: '512x512', type: 'image/png' },
        { src: './img/icons/icon-256x256.png', sizes: '256x256', type: 'image/png' },
        { src: './img/icons/icon-192x192.png', sizes: '192x192', type: 'image/png' },
        { src: './img/icons/icon-168x168.png', sizes: '168x168', type: 'image/png' },
        { src: './img/icons/icon-152x152.png', sizes: '152x152', type: 'image/png' },
        { src: './img/icons/icon-144x144.png', sizes: '144x144', type: 'image/png' },
        { src: './img/icons/icon-128x128.png', sizes: '128x128', type: 'image/png' },
        { src: './img/icons/icon-96x96.png', sizes: '96x96', type: 'image/png' },
        { src: './img/icons/icon-72x72.png', sizes: '72x72', type: 'image/png' },
        { src: './img/icons/icon-64x64.png', sizes: '64x64', type: 'image/png' },
        { src: './img/icons/icon-48x48.png', sizes: '48x48', type: 'image/png' },
        { src: './img/icons/icon-32x32.png', sizes: '32x32', type: 'image/png' },
        { src: './img/icons/icon-16x16.png', sizes: '16x16', type: 'image/png' },
      ],
    },
    iconPaths: {
      favicon32: 'img/icons/icon-32x32.png',
      favicon16: 'img/icons/icon-16x16.png',
      appleTouchIcon: 'img/icons/icon-152x152.png',
      maskIcon: 'img/icons/icon.svg',
      msTileImage: 'img/icons/icon-144x144.png',
    },
    workboxOptions: {
      runtimeCaching: [
        {
          urlPattern: ({ url }) => url.pathname == '/my-url-test/1',
          method: 'GET',
          handler: 'StaleWhileRevalidate',
          options: {
            cacheName: 'myAppCache',
          },
        },
        {
          urlPattern: ({ url }) => url.pathname == '/person',
          method: 'POST',
          handler: 'NetworkOnly',
          options: {
            backgroundSync: {
              name: 'myQueueName',
            },
          },
        },
      ],
    },
  },
};

No exemplo acima, mostra como será configurado seu arquivo manifest.json (com descrição, cores e ícones) e também como definir as rotas e comportamentos que serão interceptadas pelo Service Worker.

A partir de agora é só criar seus componentes Vue.js normalmente. As demais opções o Vue.js e o Workbox cuidam para você.

PWA: Muitas possibilidades

Por fim, quero deixar alguns links sobre assuntos que não vou me aprofundar neste momento, mas talvez você tenha interesse.

Algo bem comum nos Progressive Web App é a necessidade de armazenar dados no navegador e para isso separei dois excelentes artigos:

Além disso, também quero deixar o link do site WhatWebCanDo.today, que mostra vários recursos disponíveis no navegador e muitas vezes nós nem sabemos. Vale a pena conferir.

Caso você queira publicar um PWA na Apple Store, Play Store ou Microsoft Store, tem um tutorial no site WindowStream que é bem interessante, segue o link.

Dúvidas ou sugestões é só entrar em contato. Abraço.

Autor
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." Martin Fowler