kb.erickguedes.com
HTML5: Estrutura e Semântica Web

Armazenamento no Navegador

Aula 6 de 6

Web Storage

HTML5 introduziu localStorage e sessionStorage para armazenamento chave-valor no navegador.

// localStorage - persistente entre sessões
localStorage.setItem('tema', 'escuro');
localStorage.setItem('usuario', JSON.stringify({ nome: 'João', id: 1 }));

const tema = localStorage.getItem('tema');
const usuario = JSON.parse(localStorage.getItem('usuario'));

localStorage.removeItem('tema');
localStorage.clear();

// sessionStorage - limpo ao fechar a aba
sessionStorage.setItem('carrinho', JSON.stringify(['item1', 'item2']));

// Storage event (dispara em OUTRAS abas do mesmo domínio)
window.addEventListener('storage', (e) => {
  console.log(`Chave "${e.key}" alterada de "${e.oldValue}" para "${e.newValue}"`);
});
// Helper para storage com tipo seguro
function salvarPreferencias(prefs) {
  localStorage.setItem('preferencias', JSON.stringify(prefs));
}

function carregarPreferencias() {
  try {
    return JSON.parse(localStorage.getItem('preferencias')) || {};
  } catch {
    return {};
  }
}

IndexedDB

Banco de dados NoSQL no navegador para dados estruturados e grandes volumes.

// Abrir banco
const request = indexedDB.open('MeuBanco', 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;

  // Criar object store
  const store = db.createObjectStore('produtos', {
    keyPath: 'id',
    autoIncrement: true
  });

  // Criar índices
  store.createIndex('nome', 'nome', { unique: false });
  store.createIndex('preco', 'preco', { unique: false });
  store.createIndex('categoria', ['categoria', 'nome'], { unique: false });
};

request.onsuccess = (event) => {
  const db = event.target.result;
  console.log('Banco aberto:', db.name);
};

// Inserir dados (transação)
function adicionarProduto(produto) {
  const request = indexedDB.open('MeuBanco', 1);

  request.onsuccess = (event) => {
    const db = event.target.result;
    const tx = db.transaction('produtos', 'readwrite');
    const store = tx.objectStore('produtos');
    store.add(produto);

    tx.oncomplete = () => {
      console.log('Produto adicionado com sucesso');
      db.close();
    };
  };
}

// Consultar por índice
function buscarPorCategoria(categoria) {
  const request = indexedDB.open('MeuBanco', 1);

  request.onsuccess = (event) => {
    const db = event.target.result;
    const tx = db.transaction('produtos', 'readonly');
    const store = tx.objectStore('produtos');
    const index = store.index('categoria');
    const range = IDBKeyRange.only(categoria);

    index.getAll(range).onsuccess = (e) => {
      console.log('Produtos:', e.target.result);
      db.close();
    };
  };
}

Cache API

Interface programática para cache de requisições HTTP, usada com Service Workers.

// Adicionar ao cache
async function cacheRecurso(url) {
  const cache = await caches.open('api-v1');
  await cache.add(url);
}

// Cache com requisição customizada
async function cacheComOpcoes(url) {
  const cache = await caches.open('api-v1');
  const response = await fetch(url, {
    headers: { 'X-Cache': 'force' }
  });
  cache.put(url, response.clone());
  return response;
}

// Recuperar do cache
async function getFromCache(url) {
  const cache = await caches.open('api-v1');
  const response = await cache.match(url);
  return response || fetch(url);
}

// Estratégia Stale-While-Revalidate
async function swrStrategy(url) {
  const cache = await caches.open('dynamic');
  const cached = await cache.match(url);

  const fetchPromise = fetch(url).then(response => {
    cache.put(url, response.clone());
    return response;
  });

  return cached || fetchPromise;
}

Service Workers

// Registrar Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(reg => console.log('SW registrado:', reg.scope))
    .catch(err => console.error('Erro SW:', err));
}

// Verificar atualizações
navigator.serviceWorker.register('/service-worker.js').then(reg => {
  reg.addEventListener('updatefound', () => {
    const novoSW = reg.installing;
    novoSW.addEventListener('statechange', () => {
      if (novoSW.state === 'installed' && navigator.serviceWorker.controller) {
        console.log('Nova versão disponível! Atualize a página.');
      }
    });
  });
});

PWA Manifest

{
  "name": "Meu App PWA",
  "short_name": "MeuApp",
  "description": "Exemplo de Progressive Web App",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3498db",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}
<link rel="manifest" href="/manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

Lab: PWA To-Do List

Crie um PWA funcional com service worker, cache offline e manifest. Use IndexedDB para persistência.

# Testar offline
# DevTools > Network > Marcar "Offline"

# Verificar cache
# DevTools > Application > Cache Storage

# Verificar Service Worker
# DevTools > Application > Service Workers

Armazenamento no navegador evoluiu de cookies para um ecossistema completo: Web Storage para sessões, IndexedDB para dados estruturados, Cache API para assets e Service Workers para offline.