# 📘 SPA e PWA — Single Page Application e Progressive Web App
## Material Completo para Concursos — Estilo IDECAN
> **Banca:** IDECAN | **Área:** Tecnologia da Informação | **Nível:** Analista
>
> ⚡ **Perfil da Banca:** Citado literalmente no edital UNILAB 2026. Prioridade **média** no Bloco 1 (Dev Core). A IDECAN cobra diferenças conceituais SPA×MPA×PWA, os 3 componentes obrigatórios do PWA, ciclo de vida do Service Worker e estratégias de cache por cenário.
---
## 📋 Índice
1. [MPA — Multi-Page Application](#1-mpa--multi-page-application-modelo-tradicional)
2. [SPA — Single Page Application](#2-spa--single-page-application)
3. [MPA vs SPA — Comparativo](#3-mpa-vs-spa--comparativo)
4. [SSR, SSG e Hydration](#4-ssr-ssg-e-hydration)
5. [Client-Side Routing e Code Splitting](#5-client-side-routing-e-code-splitting)
6. [PWA — Progressive Web App](#6-pwa--progressive-web-app)
7. [Service Worker — Ciclo de Vida](#7-service-worker--ciclo-de-vida)
8. [Web App Manifest](#8-web-app-manifest)
9. [Estratégias de Cache](#9-estratégias-de-cache)
10. [Pegadinhas e Pontos-Chave da IDECAN](#10-pegadinhas-e-pontos-chave-da-idecan)
11. [Questões Comentadas no Estilo IDECAN](#11-questões-comentadas-no-estilo-idecan)
---
## 1. MPA — Multi-Page Application (modelo tradicional)
### Como funciona
No modelo **MPA**, cada navegação gera uma requisição HTTP ao servidor, que responde com uma página HTML **completa**. O navegador descarrega a página atual e carrega a nova inteiramente.
```
Usuário clica em link
→ Requisição HTTP ao servidor
→ Servidor gera e retorna HTML completo
→ Navegador recarrega a página inteira (flash branco)
→ Nova página é exibida
```
### Características
- Cada URL corresponde a um arquivo/rota no servidor que retorna HTML completo
- Recarregamento total a cada navegação
- SEO **nativo** — o servidor entrega HTML pronto para os crawlers
- Mais simples de desenvolver para casos básicos
- **Exemplos:** sistemas em PHP + Blade, JSP, Django templates, Ruby on Rails
---
## 2. SPA — Single Page Application
### Como funciona
No modelo **SPA**, uma única página HTML é carregada na primeira visita com todo o JavaScript da aplicação. Navegações subsequentes são feitas via **JavaScript** (AJAX/Fetch) — só os dados mudam, a página **não é recarregada**.
```
Primeira visita:
→ Baixa app completa (HTML + JS bundle + CSS)
→ Renderiza UI inicial
Navegações seguintes:
→ JS intercepta o clique no link
→ Busca apenas os dados necessários (JSON via API)
→ Atualiza apenas as partes do DOM que mudaram
→ URL muda via History API (sem req. ao servidor)
```
### Frameworks SPA Populares
- **React** (Meta/Facebook) — biblioteca de UI, Virtual DOM
- **Angular** (Google) — framework completo, TypeScript
- **Vue.js** — framework progressivo, MVVM
- **Svelte** — compila para JS puro (sem Virtual DOM em runtime)
### SPA e Back-end — Separação de Responsabilidades
```
┌─────────────────────────────┐ API REST/GraphQL ┌──────────────────────────┐
│ Front-end SPA │ ←────────────────────→ │ Back-end (API) │
│ React / Vue / Angular │ JSON via Fetch │ Spring Boot / Laravel │
│ - Renderiza UI │ │ - Retorna JSON │
│ - Gerencia estado │ │ - NÃO gera HTML │
│ - Roteamento no cliente │ │ - Reutilizável │
└─────────────────────────────┘ └──────────────────────────┘
```
O back-end torna-se uma **API pura** — o mesmo back-end pode servir a SPA, apps mobile e terceiros.
---
## 3. MPA vs SPA — Comparativo
| Característica | MPA | SPA |
|---|---|---|
| **Carregamento inicial** | Rápido (só a página atual) | Lento (baixa app inteira) |
| **Navegação posterior** | Lenta (recarrega tudo) | Rápida (só dados JSON) |
| **SEO** | Nativo (HTML pronto no servidor) | Desafiador (HTML gerado no cliente) |
| **Experiência do usuário** | Flash a cada navegação | Fluida, sem recarregamento |
| **Complexidade back-end** | Alta (gera HTML completo) | Baixa (só retorna JSON) |
| **Complexidade front-end** | Baixa | Alta (framework JS obrigatório) |
| **Roteamento** | Servidor | Cliente (History API) |
| **Estado da aplicação** | No servidor (sessão) | No cliente (Redux, Zustand) |
| **Exemplos** | PHP + Blade, JSP, Django | React, Angular, Vue |
> ⚠️ **IDECAN cobra:** No SPA, o carregamento **inicial** é mais **lento** (baixa toda a aplicação JS). As navegações **subsequentes** são mais rápidas. O SEO é o principal **desafio** das SPAs — o HTML inicial está quase vazio, dificultando indexação por buscadores.
---
## 4. SSR, SSG e Hydration
### Por que existem — O problema de SEO do SPA
O HTML inicial de uma SPA é quase vazio:
```html
<!-- HTML inicial de uma SPA típica -->
<html>
<body>
<div id="root"></div> <!-- vazio! -->
<script src="/bundle.js"></script> <!-- conteúdo gerado pelo JS -->
</body>
</html>
```
Os crawlers de busca (Google, Bing) têm dificuldade para indexar conteúdo gerado por JavaScript.
### SSR — Server-Side Rendering
O servidor **pré-renderiza** o HTML da SPA antes de enviar ao cliente. O usuário vê o conteúdo imediatamente e o JS assume o controle depois (hydration).
```
Cliente solicita /produto/123
→ Servidor executa React/Vue no servidor
→ Gera HTML completo com os dados do produto
→ Envia HTML pronto ao cliente (SEO + velocidade)
→ JS do cliente faz hydration (torna a página interativa)
```
**Frameworks SSR:** Next.js (React), Nuxt.js (Vue), Angular Universal, SvelteKit
### SSG — Static Site Generation
O HTML é gerado em **build-time** (não a cada requisição). Arquivos estáticos servidos por CDN — muito rápido.
```
Build: npm run build → gera /produto/1.html, /produto/2.html...
Deploy: arquivos estáticos no CDN
Requisição: CDN retorna HTML pré-gerado instantaneamente
```
**Ideal para:** blogs, documentação, landing pages (conteúdo que muda raramente).
### Hydration
Processo onde o JavaScript do framework "ativa" o HTML estático pré-renderizado pelo servidor, tornando-o **interativo**.
```
1. Servidor envia HTML pré-renderizado (página visível imediatamente)
2. Navegador recebe e exibe o HTML
3. JS da aplicação é baixado e executado
4. Framework "hidrata" — vincula eventos ao HTML existente
5. Página torna-se interativa (cliques, formulários funcionam)
```
> ⚠️ **IDECAN cobra:** SSR resolve o problema de SEO das SPAs gerando HTML no servidor. Next.js é o framework SSR mais cobrado no contexto de React.
---
## 5. Client-Side Routing e Code Splitting
### History API — Roteamento no Cliente
```javascript
// O SPA usa a History API do navegador para mudar URLs sem recarregar
// Adiciona uma entrada no histórico de navegação
history.pushState({ pagina: 'produtos' }, '', '/produtos');
// Substitui a entrada atual (sem criar nova entrada no histórico)
history.replaceState({ pagina: 'home' }, '', '/');
// Evento disparado ao clicar "Voltar"/"Avançar" no navegador
window.addEventListener('popstate', (event) => {
console.log('Navegou para:', event.state);
});
```
### React Router — Roteamento em React
```jsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
{/* Link não recarrega a página — usa pushState internamente */}
<Link to="/produtos">Produtos</Link>
<Link to="/sobre">Sobre</Link>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/produtos" element={<ListaProdutos />} />
<Route path="/produto/:id" element={<DetalheProduto />} />
<Route path="*" element={<PaginaNaoEncontrada />} />
</Routes>
</BrowserRouter>
);
}
// Ao clicar no Link, a URL muda mas a página NÃO é recarregada
```
### Code Splitting — Dividindo o Bundle
**Problema:** SPAs podem ter bundles JS enormes (MBs), tornando o carregamento inicial muito lento.
**Solução:** dividir o código em partes (chunks) que são baixadas **sob demanda**.
```jsx
import React, { lazy, Suspense } from 'react';
// Carregamento lazy — o módulo só é baixado quando a rota é acessada
const DetalheProduto = lazy(() => import('./pages/DetalheProduto'));
const Relatorios = lazy(() => import('./pages/Relatorios'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Carregando...</div>}>
<Routes>
<Route path="/produto/:id" element={<DetalheProduto />} />
<Route path="/relatorios" element={<Relatorios />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
// DetalheProduto.js só é baixado quando o usuário navega para /produto/...
```
---
## 6. PWA — Progressive Web App
### O que é PWA
**PWA (Progressive Web App)** é um conjunto de **tecnologias e padrões** que permitem que uma aplicação web ofereça experiência similar a um app nativo: funciona offline, pode ser instalada na tela inicial e recebe notificações push. Termo cunhado por Alex Russell (Google) em 2015.
### Os 4 Pilares do PWA
| Pilar | Descrição |
|---|---|
| **Confiável** | Funciona offline ou com rede instável. Service Worker cacheia recursos essenciais. |
| **Rápido** | Responde imediatamente às interações. Assets cacheados localmente. |
| **Envolvente (Engaging)** | Instalável na tela inicial. Push Notifications. Fullscreen. Experiência app-like. |
| **Segura** | HTTPS obrigatório. Service Worker só funciona em origens seguras. |
### Os 3 Componentes Obrigatórios do PWA ⭐
```
PWA = HTTPS + Service Worker + Web App Manifest
```
1. **HTTPS** — obrigatório para segurança; pré-requisito para o Service Worker funcionar
2. **Service Worker** — script em background que habilita offline e cache
3. **Web App Manifest** — arquivo JSON que permite instalação e define aparência
> ⚠️ **IDECAN cobra:** Os 3 componentes são **HTTPS + Service Worker + Web App Manifest**. A ausência de qualquer um impede que a aplicação seja classificada como PWA completo. HTTPS é pré-requisito para o Service Worker funcionar.
### PWA vs App Web vs App Nativo
| Característica | App Web | PWA | App Nativo |
|---|---|---|---|
| **Funciona offline** | Não | Sim | Sim |
| **Instalável** | Não | Sim (tela inicial) | Sim (App Store) |
| **Push Notifications** | Não | Sim | Sim |
| **Distribuição** | URL | URL (sem App Store) | App Store / Play Store |
| **Atualização** | Automática | Automática | Manual (aprovação) |
| **Acesso a hardware** | Limitado | Parcial (câmera, GPS) | Completo |
| **Performance** | Menor | Boa | Máxima |
> ⚠️ **IDECAN cobra:** PWA **não precisa** de App Store/Play Store para ser instalado — é instalado diretamente pelo navegador a partir da URL. Esta é uma das principais vantagens sobre apps nativos.
---
## 7. Service Worker — Ciclo de Vida
### O que é Service Worker
Script JavaScript que roda em uma **thread separada** da página (background), **sem acesso ao DOM**. Atua como um **proxy de rede** — intercepta todas as requisições feitas pela aplicação e decide o que fazer: buscar da rede, retornar do cache, ou combinar ambos.
### Características Importantes
- Roda em **background**, mesmo com a página fechada
- **Não tem acesso ao DOM** — usa `postMessage` para comunicar com a página
- Só funciona em **HTTPS** (exceto localhost para desenvolvimento)
- É um Worker baseado em **eventos** (event-driven)
- Tem acesso à **Cache API** para armazenar requisições/respostas
### Ciclo de Vida Completo ⭐
#### Fase 1 — Registration (Registro)
```javascript
// Na página principal (index.html ou App.js)
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js') // caminho para o arquivo do SW
.then(reg => console.log('SW registrado, escopo:', reg.scope))
.catch(err => console.error('Falha no registro:', err));
}
```
#### Fase 2 — Install (Instalação) — momento do pre-caching
```javascript
// Dentro do arquivo sw.js
const CACHE_NAME = 'meu-app-v1';
const ARQUIVOS_PARA_CACHE = [
'/',
'/index.html',
'/app.js',
'/styles.css',
'/icons/logo.png'
];
self.addEventListener('install', event => {
console.log('SW: instalando...');
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('SW: realizando pre-caching dos arquivos essenciais');
return cache.addAll(ARQUIVOS_PARA_CACHE);
})
);
// self.skipWaiting() força ativação imediata (pula a espera)
});
```
#### Fase 3 — Activate (Ativação) — momento da limpeza
```javascript
self.addEventListener('activate', event => {
console.log('SW: ativando...');
event.waitUntil(
caches.keys().then(nomesDeCache =>
Promise.all(
nomesDeCache
.filter(nome => nome !== CACHE_NAME) // caches de versões antigas
.map(nome => {
console.log('SW: removendo cache antigo:', nome);
return caches.delete(nome);
})
)
)
);
// self.clients.claim() faz o SW assumir controle imediatamente
});
```
#### Fase 4 — Fetch (Operação Normal) — interceptação de requisições
```javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(respostaDoCche => {
if (respostaDoCche) {
return respostaDoCche; // retorna do cache
}
return fetch(event.request); // busca na rede
})
);
});
```
### Outros Eventos do Service Worker
| Evento | Quando dispara | Uso |
|---|---|---|
| `push` | Mensagem push recebida do servidor | Exibir notificação push |
| `notificationclick` | Usuário clica na notificação | Abrir URL específica |
| `sync` | Conexão restabelecida | Background Sync — enviar dados enfileirados offline |
| `message` | Página envia postMessage | Comunicação bidirecional com a página |
### Resumo do Ciclo de Vida
```
Register → Install (pre-caching) → Activate (limpar caches antigos)
↓
Fetch (interceptar requisições)
Push (notificações)
Sync (background sync)
```
> ⚠️ **IDECAN cobra:** A ordem é **Register → Install → Activate → Fetch**. O evento **install** é para **pre-caching**. O evento **activate** é para **limpeza de caches antigos**. O evento **fetch** intercepta requisições em operação normal.
---
## 8. Web App Manifest
### O que é
Arquivo **JSON** (`manifest.json`) que fornece informações sobre o PWA ao navegador e ao SO: nome, ícones, cores, modo de exibição. Permite que o usuário **instale o app** na tela inicial com aparência nativa.
### Exemplo Completo
```json
{
"name": "Meu App Completo",
"short_name": "MeuApp",
"description": "Aplicação PWA de exemplo para concurso",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#ffffff",
"theme_color": "#1D9E75",
"lang": "pt-BR",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"shortcuts": [
{
"name": "Novo Pedido",
"url": "/pedidos/novo",
"icons": [{ "src": "/icons/new.png", "sizes": "96x96" }]
}
]
}
```
### Propriedade `display` — Modos de Exibição ⭐
| Valor | Descrição | Uso |
|---|---|---|
| **`standalone`** | Remove barra de endereço. Mantém barra de status do SO. Parece app nativo. | **Mais comum em PWAs** |
| `fullscreen` | Remove toda a UI do navegador e do SO. Tela inteira. | Jogos |
| `minimal-ui` | Controles mínimos de navegação (voltar, recarregar). Sem barra de endereço. | Intermediário |
| `browser` | Abre no navegador normalmente, com interface completa. | Padrão — sem experiência app-like |
> ⚠️ **IDECAN cobra:** O modo **`standalone`** é o que dá a experiência "app-like" ao PWA — remove a barra de endereço do navegador. É o valor mais cobrado. `fullscreen` é para jogos e remove absolutamente tudo.
### Como Referenciar no HTML
```html
<!-- No <head> do index.html -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#1D9E75">
<!-- Meta tags para iOS (Safari tem suporte parcial ao manifest) -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="MeuApp">
<link rel="apple-touch-icon" href="/icons/icon-192.png">
```
---
## 9. Estratégias de Cache
### Cache API — Armazenamento de Requisições
```javascript
// Abrir/criar um cache pelo nome
const cache = await caches.open('meu-cache-v1');
// Armazenar uma resposta manualmente
await cache.put(request, response);
// Buscar e armazenar automaticamente
await cache.add('/pagina.html');
await cache.addAll(['/app.js', '/styles.css', '/logo.png']);
// Buscar no cache
const resposta = await cache.match(request);
// Remover um item
await cache.delete(request);
// Listar todos os caches existentes
const nomesDosCaches = await caches.keys();
// Deletar um cache inteiro
await caches.delete('meu-cache-v1');
```
### As 5 Estratégias de Cache ⭐
#### 1. Cache First (cache → rede)
Busca no cache primeiro. Se não encontrar, vai à rede. Ideal para assets **estáticos** que raramente mudam.
```javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request).then(response => {
// opcional: salvar no cache ao buscar da rede
caches.open(CACHE_NAME).then(cache => cache.put(event.request, response.clone()));
return response;
});
})
);
});
```
**Quando usar:** imagens, fontes, CSS, JS da aplicação, ícones.
#### 2. Network First (rede → cache)
Tenta a rede primeiro. Se falhar (offline), usa o cache como fallback.
```javascript
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request)
.then(response => {
// salva a resposta no cache para uso offline
const responseClone = response.clone();
caches.open(CACHE_NAME).then(cache => cache.put(event.request, responseClone));
return response;
})
.catch(() => caches.match(event.request)) // fallback offline
);
});
```
**Quando usar:** feeds de notícias, dados de perfil, conteúdo dinâmico que precisa estar atualizado.
#### 3. Stale While Revalidate
Retorna do cache **imediatamente** (rápido) e, em paralelo, atualiza o cache com a versão mais recente da rede. Na próxima requisição, o conteúdo já estará atualizado.
```javascript
self.addEventListener('fetch', event => {
event.respondWith(
caches.open(CACHE_NAME).then(cache =>
cache.match(event.request).then(cached => {
// atualiza o cache em segundo plano
const networkFetch = fetch(event.request).then(response => {
cache.put(event.request, response.clone());
return response;
});
// retorna o cache imediatamente (se existir) ou aguarda a rede
return cached || networkFetch;
})
)
);
});
```
**Quando usar:** avatares de usuário, páginas que mudam com frequência moderada, APIs que toleram dados ligeiramente desatualizados.
#### 4. Cache Only
Sempre usa o cache. Nunca vai à rede. Para recursos **pre-cacheados** no install que não mudam.
```javascript
self.addEventListener('fetch', event => {
event.respondWith(caches.match(event.request));
});
```
**Quando usar:** app shell (HTML/CSS/JS core), assets de versão fixada.
#### 5. Network Only
Sempre usa a rede. Nunca cacheia. Para requisições que **não devem** ser cacheadas.
```javascript
self.addEventListener('fetch', event => {
event.respondWith(fetch(event.request));
});
```
**Quando usar:** analytics, pagamentos, dados em tempo real críticos, requisições POST de escrita.
### Tabela de Decisão — Qual Estratégia Usar?
| Cenário | Estratégia Recomendada |
|---|---|
| Imagens, fontes, CSS, JS | Cache First |
| Feed de notícias, dados dinâmicos | Network First |
| Perfil do usuário, thumbnails | Stale While Revalidate |
| HTML/CSS do app shell (core) | Cache Only |
| Pagamentos, analytics, POST | Network Only |
| Offline + dados mais recentes possíveis | Network First |
| Velocidade máxima + eventual atualização | Stale While Revalidate |
> ⚠️ **IDECAN cobra:** Questões apresentam cenários e pedem qual estratégia aplicar. "Usuário offline, precisa ver último conteúdo" → **Network First**. "Assets estáticos, raramente mudam" → **Cache First**. "Equilíbrio velocidade + atualização" → **Stale While Revalidate**.
### Workbox — Biblioteca do Google para Service Workers
```javascript
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
// Imagens — Cache First com expiração de 30 dias
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'imagens',
plugins: [new ExpirationPlugin({ maxAgeSeconds: 30 * 24 * 60 * 60 })]
})
);
// API REST — Network First
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({ cacheName: 'api-cache' })
);
// CSS e JS — Stale While Revalidate
registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script',
new StaleWhileRevalidate({ cacheName: 'assets' })
);
```
---
## 10. Pegadinhas e Pontos-Chave da IDECAN
### ❌ Afirmações FALSAS (distratores clássicos)
1. ~~"No SPA, o carregamento inicial é mais rápido que no MPA"~~ → **FALSO**. O carregamento **inicial** do SPA é mais **lento** (baixa toda a aplicação JS). As navegações **subsequentes** são mais rápidas.
2. ~~"Um PWA precisa ser publicado na App Store ou Google Play para ser instalado"~~ → **FALSO**. PWA é instalado **diretamente pelo navegador** a partir da URL, sem loja de apps.
3. ~~"O Service Worker tem acesso direto ao DOM para manipulá-lo"~~ → **FALSO**. Service Worker **não tem acesso ao DOM** — roda em thread separada. Para comunicar com a página usa **`postMessage`**.
4. ~~"Service Workers podem ser registrados em origens HTTP simples"~~ → **FALSO**. Service Workers **só funcionam em HTTPS** (exceto localhost para desenvolvimento).
5. ~~"O evento 'install' do SW é o momento para limpar caches antigos"~~ → **FALSO**. Limpeza de caches antigos deve ocorrer no **'activate'**. O **'install'** é para **pre-caching** dos arquivos essenciais.
6. ~~"O modo 'fullscreen' no manifest é o mais usado para dar experiência app-like"~~ → **FALSO**. O modo mais comum em PWAs é **'standalone'**. `fullscreen` é para jogos.
7. ~~"Cache First é ideal para APIs REST com dados dinâmicos"~~ → **FALSO**. Cache First é para **assets estáticos**. Para APIs dinâmicas usa-se **Network First** ou Stale While Revalidate.
8. ~~"SPA e PWA são a mesma coisa"~~ → **FALSO**. São conceitos **independentes e complementares**. SPA = arquitetura de navegação. PWA = conjunto de tecnologias para offline/app-like. Um SPA pode ou não ser um PWA.
9. ~~"O carregamento SSR é exclusivo de MPAs — SPAs não podem usar SSR"~~ → **FALSO**. **Next.js** e **Nuxt.js** fazem SSR de SPAs React e Vue, respectivamente, resolvendo o problema de SEO.
10. ~~"Service Worker é um componente opcional do PWA"~~ → **FALSO**. Service Worker é um dos **3 componentes obrigatórios** — sem ele não há offline e a aplicação não é um PWA completo.
### ✅ Afirmações VERDADEIRAS (fixe!)
1. Os 3 componentes obrigatórios do PWA são **HTTPS + Service Worker + Web App Manifest**.
2. O ciclo de vida do Service Worker é **Register → Install → Activate → Fetch**.
3. O evento **`install`** é o momento do **pre-caching** (cachear assets essenciais).
4. O evento **`activate`** é o momento de **limpar caches antigos**.
5. O modo **`standalone`** no manifest remove a barra de endereço e dá experiência app-like.
6. SPA tem SEO desafiador porque o HTML inicial está quase vazio — conteúdo é gerado por JS no cliente.
7. **Next.js** resolve o SEO das SPAs React usando SSR (Server-Side Rendering).
8. **Stale While Revalidate**: retorna do cache imediatamente E atualiza o cache em segundo plano.
9. PWA não requer App Store — é instalado pelo navegador diretamente a partir da URL.
10. Service Worker só funciona em **HTTPS** (ou localhost para desenvolvimento).
11. **Code splitting** divide o bundle JS em chunks carregados sob demanda — reduz tempo de carregamento inicial do SPA.
12. No Web App Manifest, o campo **`start_url`** define qual URL abre quando o usuário lança o app instalado.
---
## 11. Questões Comentadas no Estilo IDECAN
---
### Questão 1
**Qual é a principal diferença entre SPA e MPA no carregamento de páginas?**
- A) No SPA, cada navegação gera uma requisição HTTP que retorna HTML completo do servidor.
- B) No SPA, uma única página HTML é carregada inicialmente e o JS atualiza o DOM com dados via AJAX; no MPA, cada navegação recarrega a página completa do servidor.
- C) SPA e MPA são equivalentes — a diferença é apenas o framework utilizado.
- D) No SPA, o SEO é mais eficiente que no MPA porque o conteúdo é gerado no cliente.
- E) No MPA, o JavaScript controla toda a navegação sem recarregar páginas.
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: B**
**Comentário:** No SPA, uma única página HTML é carregada com todo o JavaScript. Navegações subsequentes atualizam apenas partes do DOM via AJAX/Fetch, sem recarregar a página. No MPA, cada clique gera uma requisição HTTP ao servidor que retorna HTML completo — a página recarrega inteiramente. O SEO é o principal **desafio** (não vantagem) do SPA, pois o HTML inicial está quase vazio.
</details>
---
### Questão 2
**Quais são os 3 componentes obrigatórios para que uma aplicação seja classificada como PWA completa?**
- A) React.js, Service Worker e localStorage
- B) HTTPS, Service Worker e Web App Manifest
- C) Service Worker, WebSocket e IndexedDB
- D) Web App Manifest, Push Notifications e HTTPS
- E) Angular, Service Worker e Cache API
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: B**
**Comentário:** Os 3 componentes obrigatórios são: (1) **HTTPS** — obrigatório para segurança e pré-requisito do Service Worker; (2) **Service Worker** — proxy de rede que habilita offline e cache; (3) **Web App Manifest** — arquivo JSON que permite instalação e define aparência app-like. React, WebSocket, Push Notifications e IndexedDB são opcionais. A alternativa D troca Service Worker por Push Notifications, que é apenas uma funcionalidade do SW.
</details>
---
### Questão 3
**Sobre o Service Worker, assinale a alternativa CORRETA:**
- A) O Service Worker tem acesso direto ao DOM para atualizar elementos da página.
- B) O Service Worker pode ser registrado em origens HTTP simples para facilitar o desenvolvimento.
- C) O Service Worker roda em thread separada, sem acesso ao DOM, e intercepta requisições de rede.
- D) O Service Worker substitui o localStorage para armazenamento persistente offline.
- E) O Service Worker é executado na mesma thread da página, compartilhando o contexto JavaScript.
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: C**
**Comentário:** O Service Worker roda em uma thread separada (background), completamente independente da página. **Não tem acesso ao DOM** — usa `postMessage` para comunicar com a página. Intercepta requisições de rede (evento `fetch`) e decide o que retornar (cache ou rede). Só funciona em **HTTPS** (exceto localhost). Não é substituto do localStorage — é um proxy de rede com Cache API.
</details>
---
### Questão 4
**No ciclo de vida do Service Worker, em qual evento deve ser realizado o pre-caching dos arquivos essenciais da aplicação?**
- A) No evento `register` — antes de instalar o SW
- B) No evento `install` — quando o SW é instalado pela primeira vez
- C) No evento `activate` — quando o SW assume o controle das páginas
- D) No evento `fetch` — quando a primeira requisição é interceptada
- E) No evento `push` — quando uma notificação push é recebida
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: B**
**Comentário:** O evento **`install`** é disparado quando o Service Worker é instalado pela primeira vez — é o momento ideal para o **pre-caching** dos arquivos essenciais (HTML, CSS, JS, ícones) usando `event.waitUntil()` com `caches.addAll()`. O evento **`activate`** é para limpar caches de versões anteriores. O evento **`fetch`** é para interceptar requisições durante o uso normal.
</details>
---
### Questão 5
**Uma aplicação de notícias deve exibir conteúdo atualizado sempre que online, mas também mostrar as últimas notícias quando offline. Qual estratégia de cache é mais adequada?**
- A) Cache First — retorna do cache, nunca vai à rede
- B) Cache Only — usa apenas o cache pré-definido
- C) Network First — tenta a rede primeiro, usa o cache como fallback quando offline
- D) Stale While Revalidate — retorna do cache e atualiza em segundo plano
- E) Network Only — sempre usa a rede, sem fallback
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: C**
**Comentário:** **Network First** é a estratégia correta para conteúdo dinâmico (notícias) que precisa estar atualizado quando online, mas disponível offline. O SW tenta a rede primeiro — com conexão, retorna dados frescos e atualiza o cache. Sem conexão, usa o cache como fallback. Cache First priorizaria dados antigos mesmo com internet. Stale While Revalidate retornaria o cache imediatamente (possivelmente desatualizado) mesmo estando online.
</details>
---
### Questão 6
**Sobre o campo `display` no Web App Manifest, qual valor remove a barra de endereço do navegador e dá ao PWA aparência de aplicativo nativo?**
- A) `fullscreen` — remove toda a UI do navegador e do sistema operacional
- B) `browser` — abre no navegador com interface completa
- C) `standalone` — remove a barra de endereço mas mantém barra de status do SO
- D) `minimal-ui` — exibe apenas controles básicos de navegação
- E) `native` — emula completamente a experiência de app nativo
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: C**
**Comentário:** **`standalone`** é o modo mais usado em PWAs — remove a barra de endereço do navegador, dando experiência app-like, mas mantém a barra de status do SO (horário, bateria). `fullscreen` remove **toda** a UI (inclusive SO) e é usado em jogos. `browser` é o padrão sem experiência app-like. `minimal-ui` mantém controles básicos de navegação. `native` não é um valor válido do manifest.
</details>
---
### Questão 7 (Certo/Errado — estilo IDECAN)
*"SPA e PWA são conceitos equivalentes — uma SPA é necessariamente uma PWA, pois ambas dispensam o recarregamento de página e oferecem experiência similar a aplicativos nativos."*
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: ERRADO**
**Comentário:** SPA e PWA são conceitos **independentes e complementares**, não equivalentes. **SPA** é uma arquitetura de navegação — uma página HTML com roteamento no cliente via JavaScript. **PWA** é um conjunto de tecnologias (HTTPS + Service Worker + Manifest) que adiciona capacidades offline, instalação e notificações push. Uma SPA pode ser um PWA (Twitter Lite, por exemplo), mas não necessariamente. Uma MPA tradicional também pode ser um PWA se adicionar Service Worker e Manifest. São dimensões diferentes: SPA descreve *como a navegação funciona*; PWA descreve *quais capacidades a aplicação possui*.
</details>
---
### Questão 8 (Certo/Errado)
*"O evento `activate` do Service Worker é o momento recomendado para limpar caches de versões anteriores, pois nesta fase o SW já assumiu o controle das páginas e é seguro remover recursos que não serão mais usados."*
<details>
<summary>🔎 Ver resposta e comentário</summary>
**✅ Resposta: CERTO**
**Comentário:** Afirmação correta. O evento `activate` é disparado após o SW ser instalado e estar pronto para assumir o controle. É o momento ideal para remover caches de versões anteriores (ex: `meu-app-v1` quando a versão atual é `meu-app-v2`), garantindo que o espaço de armazenamento não seja desperdiçado com recursos obsoletos. O evento `install` (fase anterior) é para criar o novo cache com os arquivos da versão atual. Tentar deletar caches antigos no `install` pode causar problemas de concorrência com o SW anterior ainda em execução.
</details>
---
## 📌 Resumo Visual — Comparativo Final
```
SPA PWA
┌──────────────────┐ ┌──────────────────┐
│ Uma página HTML │ │ HTTPS obrigatório│
│ Roteamento no JS │ │ Service Worker │
│ AJAX para dados │ │ Web App Manifest │
│ React/Vue/Angular│ │ Offline-first │
│ Desafio: SEO │ │ Instalável │
└──────────────────┘ └──────────────────┘
↑ ↑
└──────────┬────────────────┘
│
Podem ser usados
juntos (SPA + PWA)
ou separadamente
```
---
## 📚 Tabela de Referência Rápida
| Conceito | Resumo |
|---|---|
| SPA | Uma página HTML + JS atualiza DOM via AJAX — sem recarregamento |
| MPA | Cada navegação = nova requisição HTTP + HTML completo do servidor |
| SEO no SPA | Desafiador (HTML inicial vazio) — solução: SSR com Next.js/Nuxt |
| SSR | Servidor pré-renderiza HTML da SPA — combina SEO + experiência SPA |
| Code Splitting | Divide bundle JS em chunks carregados sob demanda |
| PWA | HTTPS + Service Worker + Web App Manifest |
| Service Worker | Proxy de rede em thread separada, sem acesso ao DOM, só HTTPS |
| SW — install | Pre-caching dos arquivos essenciais |
| SW — activate | Limpeza de caches de versões antigas |
| SW — fetch | Interceptação de requisições em operação normal |
| Cache First | Cache → rede. Para assets estáticos. |
| Network First | Rede → cache (fallback offline). Para dados dinâmicos. |
| Stale While Revalidate | Cache imediato + atualiza em background. Para equilíbrio. |
| manifest display | `standalone` = app-like (sem barra de endereço) |
| PWA instalação | Via navegador, sem App Store / Play Store |
| Workbox | Biblioteca Google que abstrai Service Worker e estratégias de cache |
---
*Material elaborado com base no perfil de cobrança histórico da banca IDECAN (2006–2026).*
*Última atualização: 2026*