Um recurso de arquitetura comum dos aplicativos da Web de página única (SPA, na sigla em inglês) é um conjunto mínimo de HTML, CSS e JavaScript necessário para potencializar a funcionalidade global de um aplicativo. Na prática, isso tende a ser o cabeçalho, a navegação e outros elementos comuns da interface do usuário que persistem em todas as páginas. Quando um service worker armazena previamente em cache o HTML e os recursos dependentes dessa interface, chamamos isso de shell do aplicativo.
O shell do aplicativo desempenha um papel significativo no desempenho percebido de um aplicativo da Web. É a primeira coisa que carrega e, portanto, também é a primeira coisa que os usuários veem enquanto esperam o conteúdo preencher a interface do usuário.
Embora o shell do aplicativo seja de carregamento rápido (desde que a rede esteja disponível e pelo menos um pouco rápida), um service worker que pré-armazena em cache o shell do aplicativo e os recursos associados a ele oferece estes outros benefícios ao modelo de shell do aplicativo:
- Performance confiável e consistente em visitas repetidas. Na primeira visita a um app sem um service worker instalado, a marcação do aplicativo e os recursos associados precisam ser carregados pela rede antes que o service worker possa colocá-los em cache. Entretanto, visitas repetidas extrairão o shell do aplicativo do cache, o que significa que o carregamento e a renderização serão instantâneos.
- Acesso confiável a funcionalidades em cenários off-line. Às vezes, o acesso à Internet é irregular ou totalmente ausente, e a temida tela "não conseguimos encontrar esse site" aparece. O modelo de shell do aplicativo resolve isso respondendo a qualquer solicitação de navegação com a marcação de shell do aplicativo do cache. Mesmo que alguém visite um URL em seu aplicativo da Web que nunca tenha acessado antes, o shell do aplicativo será exibido a partir do cache e poderá ser preenchido com conteúdo útil.
Quando o modelo de shell do aplicativo precisa ser usado
Um shell do aplicativo é mais útil quando há elementos comuns da interface do usuário que não mudam de uma rota para outra, mas o conteúdo sim. A maioria dos SPAs provavelmente já usa um modelo de shell do aplicativo.
Se isso descreve seu projeto e você quiser adicionar um service worker para melhorar a confiabilidade e o desempenho, o shell do aplicativo precisa:
- Carregar rapidamente.
- Use recursos estáticos de uma instância de
Cache
. - Inclua elementos comuns da interface, como um cabeçalho e uma barra lateral, separados do conteúdo da página.
- Recupere e exiba conteúdo específico da página.
- Se apropriado, armazene em cache conteúdo dinâmico para visualização off-line.
O shell do aplicativo carrega o conteúdo específico da página de forma dinâmica por APIs ou conteúdo empacotado em JavaScript. Ela também deve ser de autoatualização no sentido de que, se a marcação do shell do aplicativo mudar, uma atualização do service worker deverá selecionar o novo shell do aplicativo e armazená-lo em cache automaticamente.
Como criar o shell do aplicativo
O shell do aplicativo deve existir independentemente do conteúdo e, ainda assim, fornecer uma base para o conteúdo ser preenchido dentro dele. O ideal é que ele seja o mais fino possível, mas inclua conteúdo significativo suficiente no download inicial para que o usuário entenda que uma experiência está sendo carregada rapidamente.
O equilíbrio certo depende do seu app. O shell do aplicativo do app Treinado para Thrill (link em inglês) de Jake Archibald inclui um cabeçalho com um botão de atualização para extrair novos conteúdos do Flickr.
A marcação do shell do aplicativo varia de projeto para projeto. Veja um exemplo de arquivo index.html
que fornece o código boilerplate do aplicativo:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
Application Shell Example
</title>
<link rel="manifest" href="/manifest.json">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="styles/global.css">
</head>
<body>
<header class="header">
<!-- Application header -->
<h1 class="header__title">Application Shell Example</h1>
</header>
<nav class="nav">
<!-- Navigation items -->
</nav>
<main id="app">
<!-- Where the application content populates -->
</main>
<div class="loader">
<!-- Spinner/content placeholders -->
</div>
<!-- Critical application shell logic -->
<script src="app.js"></script>
<!-- Service worker registration script -->
<script>
if ('serviceWorker' in navigator) {
// Register a service worker after the load event
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
</script>
</body>
</html>
Independentemente de como você cria um shell de aplicativo para seu projeto, ele precisa ter as seguintes características:
- O HTML deve ter áreas claramente isoladas para elementos individuais da interface do usuário. No exemplo acima, isso inclui o cabeçalho, a navegação, a área de conteúdo principal e o espaço do aplicativo para um "ícone de carregamento" de carregamento que só aparece quando o conteúdo está sendo carregado.
- O JavaScript e o CSS iniciais carregados para o shell do aplicativo devem ser mínimos e estar relacionados apenas à funcionalidade do próprio shell do aplicativo, não ao conteúdo. Isso garante que o aplicativo renderize o shell o mais rápido possível e minimiza o trabalho da linha de execução principal até que o conteúdo seja mostrado.
- Script in-line que registra um service worker.
Depois que o shell do aplicativo for criado, será possível criar um service worker para armazenar em cache ele e os recursos.
Como armazenar o shell do aplicativo em cache
O shell do aplicativo e os recursos necessários são os itens que o service worker precisa armazenar em cache imediatamente no momento da instalação. Considerando um shell de aplicativo como o exemplo acima, vamos ver como fazer isso em um exemplo básico de caixa de trabalho usando workbox-build
:
// build-sw.js
import {generateSW} from 'workbox-build';
// Where the generated service worker will be written to:
const swDest = './dist/sw.js';
generateSW({
swDest,
globDirectory: './dist',
globPatterns: [
// The necessary CSS and JS for the app shell
'**/*.js',
'**/*.css',
// The app shell itself
'shell.html'
],
// All navigations for URLs not precached will use this HTML
navigateFallback: 'shell.html'
}).then(({count, size}) => {
console.log(`Generated ${swDest}, which precaches ${count} assets totaling ${size} bytes.`);
});
Essa configuração armazenada em build-sw.js
importa o CSS e o JavaScript do app, incluindo o arquivo de marcação do shell do aplicativo contido em shell.html
. O script é executado com o Node da seguinte maneira:
node build-sw.js
O service worker gerado é gravado em ./dist/sw.js
e registrará a seguinte mensagem quando terminar:
Generated ./dist/sw.js, which precaches 5 assets totaling 44375 bytes.
Quando a página é carregada, o service worker armazena em cache a marcação do shell do aplicativo e as dependências correspondentes:
O armazenamento em cache prévio do HTML, CSS e JavaScript do shell do seu aplicativo é possível em quase todos os fluxos de trabalho, incluindo projetos que usam bundlers. Conforme avança na documentação, você vai aprender a usar o Workbox diretamente para configurar o conjunto de ferramentas e criar um service worker que funcione melhor para seu projeto, seja ele SPA ou não.
Conclusão
Combinar o modelo de shell do aplicativo com um service worker é ótimo para armazenamento em cache off-line, principalmente se você combinar a funcionalidade de pré-armazenamento em cache com uma estratégia que prioriza a rede e volta à estratégia de cache para marcação ou respostas de API. O resultado é uma experiência rápida e confiável que renderiza instantaneamente o shell do seu aplicativo em acessos repetidos, mesmo off-line.