Next.js e Service Workers: Guia Completo para Otimizar Seu App

Os service workers são scripts que operam em segundo plano, desempenhando um papel crucial ao fornecer funcionalidades avançadas de cache e outros recursos essenciais para aplicações web contemporâneas.

Esses mecanismos transformam a experiência do usuário, assemelhando-se à fluidez e praticidade dos aplicativos nativos diretamente no navegador web.

Os service workers são, portanto, pilares na construção de Progressive Web Apps (PWAs).

Entendendo os Service Workers

Um service worker é um tipo específico de Web Worker JavaScript, executado em segundo plano e de forma independente da thread JavaScript principal. Essa separação evita bloqueios e garante que a interface do usuário do aplicativo e a interação do usuário permaneçam responsivas, sem atrasos ou interrupções.

Na prática, os service workers atuam como servidores proxy, posicionando-se entre a aplicação web e a rede. Eles têm a capacidade de interceptar requisições e respostas, armazenar recursos em cache e oferecer suporte para acesso offline. Isso resulta em aplicações web mais integradas e fáceis de usar, mesmo quando a conexão com a internet é interrompida.

Aplicações Principais para Service Workers

A versatilidade dos service workers permite seu uso em diversas situações, incluindo:

  • PWAs: Os service workers são o motor dos Progressive Web Apps, possibilitando requisições de rede customizadas, notificações push, funcionalidade offline e carregamento ágil.
  • Cache: Eles armazenam ativos do aplicativo, como imagens, código JavaScript e arquivos CSS, no cache do navegador. Isso possibilita que o navegador recupere esses elementos diretamente do cache, ao invés de solicitá-los repetidamente do servidor. O resultado é um carregamento mais rápido do conteúdo, especialmente útil em conexões de internet lentas ou instáveis.
  • Sincronização em Segundo Plano: Os service workers podem sincronizar dados e realizar outras tarefas em segundo plano, mesmo quando o usuário não está interagindo ativamente com o aplicativo ou quando ele não está aberto no navegador.

Integrando Service Workers em Aplicações Next.js

Antes de nos aprofundarmos no código, é importante compreender o ciclo de vida de um service worker, que se divide em duas fases principais: registro e ativação.

Na primeira fase, o navegador registra o service worker. Veja um exemplo básico:

const registrarServiceWorker = async () => {
    if ("serviceWorker" in navigator) {
        registro = await navigator.serviceWorker.register("/sw.js");
    }
};
registrarServiceWorker();

Este código verifica se o navegador suporta service workers, o que é comum em todos os navegadores modernos. Caso exista suporte, o código registrará um service worker, localizado no caminho de arquivo especificado.

Na fase de ativação, é necessário instalar e ativar o service worker, escutando os eventos de instalação e ativação por meio de ouvintes de eventos JavaScript. Veja como realizar essa operação:

 registro.addEventListener("install", () => {
    console.log("Service worker instalado");
});
registro.addEventListener("activate", () => {
    console.log("Service worker ativado");
});

Este código deve ser inserido logo após o registro do service worker e executado assim que o registro for concluído.

O código completo deste projeto pode ser encontrado no repositório GitHub.

Criando um Projeto Next.js

Para começar, utilize o seguinte comando para criar a estrutura básica de um projeto Next.js localmente:

npx create-next-app next-project

A adição de um service worker a um aplicativo Next.js envolve os seguintes passos:

  • Registrar o service worker no escopo global do ambiente.
  • Criar um arquivo JavaScript para o service worker no diretório público.

Adicionando um Service Worker

Primeiramente, registre o service worker. Modifique o arquivo src/pages/_app.js como descrito abaixo. A inclusão do código neste arquivo garante que o service worker seja registrado assim que o aplicativo for carregado e que tenha acesso a todos os recursos do aplicativo.

 import { useEffect } from 'react';

export default function App({ Component, pageProps }) {
    useEffect(() => {
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker
                .register('/service-worker.js', { scope: "https://www.makeuseof.com/" })
                .then((registration) => {
                    console.log(
                        'Service worker registrado com sucesso. Escopo:',
                        registration.scope
                    );
                })
                .catch((error) => {
                    console.error('Falha no registro do service worker:', error);
                });
        }
    }, []);

    return <Component {...pageProps} />;
}

O hook useEffect é acionado quando o componente é montado. Similar ao exemplo anterior, o código primeiro verifica se o navegador do usuário oferece suporte a service workers.

Se o suporte existir, ele registra o script do service worker, localizado no caminho de arquivo especificado, e define seu escopo como “/”. Isso significa que o service worker tem controle sobre todos os recursos do aplicativo. É possível definir um escopo mais específico, como “/produtos”, se necessário.

Caso o registro seja bem-sucedido, uma mensagem de confirmação, juntamente com seu escopo, será registrada. Se ocorrer um erro durante o processo, o código irá detectá-lo e exibir uma mensagem de erro.

Instalando e Ativando o Service Worker

Adicione o seguinte código a um novo arquivo, public/service-worker.js.

const eventoInstalar = () => {
    self.addEventListener('install', () => {
        console.log('service worker instalado!!!!');
    });
};
eventoInstalar();
    
const eventoAtivar = () => {
    self.addEventListener('activate', () => {
        console.log('service worker ativado!!!');
    });
};

eventoAtivar();

Para confirmar o registro, instalação e ativação bem-sucedida do service worker, inicie o servidor de desenvolvimento e abra o aplicativo no navegador.

npm run dev

Abra a janela Ferramentas do desenvolvedor do Chrome (ou equivalente em seu navegador) e acesse a aba “Aplicativo”. Na seção “Service Workers”, você poderá ver o service worker que você registrou.

Com o service worker registrado, instalado e ativado com sucesso, você pode começar a implementar funcionalidades como cache, sincronização em segundo plano ou envio de notificações push.

Armazenando Recursos em Cache com Service Workers

O armazenamento em cache dos ativos do aplicativo no dispositivo do usuário pode otimizar o desempenho, permitindo acesso mais rápido aos recursos, especialmente em situações com conexões de internet instáveis.

Para armazenar os ativos do aplicativo em cache, insira o código a seguir no arquivo service-worker.js.

const nomeCache="teste-cache";

self.addEventListener('fetch', (event) => {
    event.respondWith(
        caches.match(event.request).then((respostaCache) => {
            return respostaCache || fetch(event.request).then((resposta) => {
                return caches.open(nomeCache).then((cache) => {
                    cache.put(event.request, resposta.clone());
                    return resposta;
                });
            });
        })
    );
});

Ao acessar a página inicial pela primeira vez, esse código verifica se existe uma resposta em cache para a requisição. Caso a resposta em cache seja encontrada, o service worker a retorna ao cliente.

Caso a resposta em cache não exista, o service worker busca o recurso do servidor através da rede, fornecendo a resposta ao cliente e armazenando-a em cache para requisições futuras.

Para visualizar os ativos armazenados em cache, abra a aba “Aplicativo” nas ferramentas do desenvolvedor. Na seção “Armazenamento em cache”, você deverá encontrar uma lista dos ativos que foram armazenados em cache. É possível também marcar a opção “Offline” na seção “Service Worker” e recarregar a página para simular uma experiência offline.

Agora, após visitar a página inicial, o navegador utilizará os recursos armazenados em cache, ao invés de solicitar dados da rede.

Utilizando Service Workers para Otimizar o Desempenho

Os service workers representam uma ferramenta eficaz para aprimorar o desempenho de aplicações Next.js. Através do armazenamento em cache de recursos, interceptação de requisições e oferecimento de suporte offline, a experiência do usuário é significativamente melhorada.

É importante salientar, no entanto, que a implementação e gestão de service workers podem apresentar certa complexidade. É crucial analisar cuidadosamente os potenciais benefícios e desvantagens antes de decidir utilizá-los.