Guia Básico sobre o uso do Cache do Aplicativo

HTML5 Rocks

Introdução

É cada vez mais importante que os aplicativos baseados na web possam ser acessados off-line. Sim, todos os navegadores possuem mecanismos de cache, mas eles não são confiáveis e nem sempre trabalham da forma esperada. O HTML5 resolve alguns dos inconvenientes de ficar off-line com a interface ApplicationCache.

O uso da interface de cache nos aplicativos oferece três vantagens:

  1. Navegação off-line: os usuários podem navegar por todo o site quando estão off-line
  2. Velocidade: os recursos armazenados em cache são locais e, portanto, são carregados mais rapidamente.
  3. Redução da carga do servidor: o navegador fará download do servidor apenas dos recursos que forem alterados.

O Cache do Aplicativo (ou AppCache) permite que o desenvolvedor especifique quais arquivos o navegador pode armazenar em cache e disponibilizar para usuários off-line. O aplicativo será carregado e funcionará corretamente, mesmo se o usuário pressionar o botão "Atualizar" enquanto estiver off-line.

O arquivo de manifesto de cache

O arquivo de manifesto de cache é um arquivo de texto simples que contém a lista de recursos que o navegador deve armazenar em cache para acesso off-line.

Como fazer referência a um arquivo de manifesto

Para ativar o cache para um aplicativo, inclua o atributo de manifesto na tag html do documento:

<html manifest="example.appcache">
  ...
</html>

O atributo manifest deve ser incluído em cada página do aplicativo da web a ser armazenado em cache. O navegador não armazena em cache uma página que não contenha o atributo manifest (exceto se estiver explicitamente listado no arquivo de manifesto). Isso significa que todas as páginas acessadas pelo usuário que contiverem manifest serão implicitamente adicionadas ao cache do aplicativo. Assim sendo, não há necessidade de incluir todas as páginas no manifesto.

O atributo manifest pode direcionar para um URL absoluto ou um caminho relativo, mas o URL absoluto deve estar na mesma origem do aplicativo da web. Um arquivo de manifesto pode ter qualquer extensão, mas precisa ser veiculado com o tipo mime correto (veja a seguir).

<html manifest="http://www.example.com/example.mf">
  ...
</html>

Um arquivo de manifesto deve ser veiculado com o tipo mime text/cache-manifest. Se for necessário, adicione um tipo de arquivo personalizado a seu servidor da Web ou à configuração .htaccess.

Por exemplo, para veicular esse tipo mime no Apache, adicione esta linha ao arquivo de configuração:

AddType text/cache-manifest .appcache

Ou, ao arquivo app.yaml do Google App Engine:

- url: /mystaticdir/(.*\.appcache)
  static_files: mystaticdir/\1
  mime_type: text/cache-manifest
  upload: mystaticdir/(.*\.appcache)

Estrutura de um arquivo de manifesto

Um manifesto simples deve ter esta aparência:

CACHE MANIFEST
index.html
stylesheet.css
images/logo.png
scripts/main.js

Este exemplo armazenará em cache quatro arquivos na página que especifica esse arquivo de manifesto.

Algumas coisas devem ser observadas:

  • A string CACHE MANIFEST é a primeira linha e é obrigatória.
  • Os sites estão limitados a 5 MB de dados armazenados em cache. No entanto, se você estiver criando um aplicativo para a Chrome Web Store, o uso de unlimitedStorage removerá essa restrição.
  • Se houver falha no download de um arquivo de manifesto ou de um recurso especificado nele, todo o processo de atualização do cache falhará. Em caso de falha, o navegador continuará a usar o cache do aplicativo antigo.

Vamos examinar um exemplo mais complexo:

CACHE MANIFEST
# 2010-06-18:v2

# Explicitly cached 'master entries'.
CACHE:
/favicon.ico
index.html
stylesheet.css
images/logo.png
scripts/main.js

# Resources that require the user to be online.
NETWORK:
login.php
/myapi
http://api.twitter.com

# static.html will be served if main.py is inaccessible
# offline.jpg will be served in place of all images in images/large/
# offline.html will be served in place of all other .html files
FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
*.html /offline.html

As linhas que começam com '#' são linhas de comentários, mas também podem servir para outros fins. O cache de um aplicativo só é atualizado quando seu arquivo de manifesto é alterado. Assim, por exemplo, se você editar um recurso de imagem ou alterar uma função javascript, essas alterações não são armazenadas em cache novamente. Modifique o arquivo de manifesto para solicitar que o navegador atualize os arquivos armazenados em cache. A criação de uma linha comentada com número de versão gerado, hash dos arquivos ou carimbo de data e hora é uma forma de garantir que os usuários tenham a versão mais recente do software. É possível também atualizar o cache de maneira programática quando uma nova versão está pronta, conforme discutido na seçãoComo atualizar o cache.

Um manifesto pode ter três seções distintas: CACHE, NETWORK e FALLBACK.

CACHE:
É a seção padrão para entradas. Os arquivos listados sob esse cabeçalho (ou imediatamente após CACHE MANIFEST) serão explicitamente armazenados em cache após o primeiro download.
NETWORK:
Os arquivos listados nessa seção são recursos seguros que necessitam de conexão com o servidor. Todas as solicitações para esses recursos ignoram o cache, mesmo que o usuário esteja off-line. Podem ser usados caracteres curingas.
FALLBACK:
Uma seção opcional que especifica páginas substitutas caso um recurso não possa ser acessado. O primeiro URI é o recurso; o segundo é o substituto. Os dois URIs devem ser relativos e ter a mesma origem do arquivo de manifesto. Podem ser usados caracteres curingas.

Observação: essas seções podem aparecer em qualquer ordem e mais de uma vez em um mesmo manifesto.

O manifesto a seguir é uma página genérica (offline.html) exibida quando o usuário tenta acessar a raiz do site enquanto está off-line. Ele também declara que todos os outros recursos (por exemplo, os de sites remotos) exigem conexão com a Internet.

CACHE MANIFEST
# 2010-06-18:v3

# Explicitly cached entries
index.html
css/style.css

# offline.html will be displayed if the user is offline
FALLBACK:
/ /offline.html

# All other resources (e.g. sites) require the user to be online. 
NETWORK:
*

# Additional resources to cache
CACHE:
images/logo1.png
images/logo2.png
images/logo3.png

Observação: o arquivo HTML que faz referência ao arquivo de manifesto é armazenado em cache automaticamente. Não há necessidade de incluí-lo no manifesto, mas é o mais recomendável.

Observação: os cabeçalhos de cache e as restrições de armazenamento em cache impostas para páginas veiculadas em SSL são substituídos por manifestos de cache. Assim, é possível criar páginas veiculadas em https para funcionarem off-line.

Como atualizar o cache

Depois que um aplicativo fica off-line, ele permanece armazenado em cache até que uma das seguintes situações ocorra:

  1. O usuário limpa o armazenamento de dados do navegador para seu site.
  2. O arquivo de manifesto é modificado. Observação: a atualização de um arquivo no manifesto não significa que o navegador armazenará novamente esse recurso em cache. O arquivo de manifesto deve ser alternado.
  3. O cache do aplicativo é atualizado de maneira programática.

Status do cache

O objeto window.applicationCache é seu acesso programático ao cache de aplicativo do navegador. A propriedade status verifica o status atual do cache:

var appCache = window.applicationCache;

switch (appCache.status) {
  case appCache.UNCACHED: // UNCACHED == 0
    return 'UNCACHED';
    break;
  case appCache.IDLE: // IDLE == 1
    return 'IDLE';
    break;
  case appCache.CHECKING: // CHECKING == 2
    return 'CHECKING';
    break;
  case appCache.DOWNLOADING: // DOWNLOADING == 3
    return 'DOWNLOADING';
    break;
  case appCache.UPDATEREADY:  // UPDATEREADY == 4
    return 'UPDATEREADY';
    break;
  case appCache.OBSOLETE: // OBSOLETE == 5
    return 'OBSOLETE';
    break;
  default:
    return 'UKNOWN CACHE STATUS';
    break;
};

Para atualizar o cache de maneira programática, chame applicationCache.update(). Será feita uma tentativa de atualizar o cache do usuário (o que exige que o arquivo de manifesto tenha sido alterado). Finalmente, quando applicationCache.status estiver no estado UPDATEREADY, a chamada de applicationCache.swapCache() trocará o cache antigo pelo novo.

var appCache = window.applicationCache;

appCache.update(); // Attempt to update the user's cache.

...

if (appCache.status == window.applicationCache.UPDATEREADY) {
  appCache.swapCache();  // The fetch was successful, swap in the new cache.
}

Observação: o uso de update() e swapCache() dessa forma não veicula os recursos atualizados aos usuários. Esse fluxo simplesmente solicita que o navegador verifique se há um novo manifesto, faça o download do conteúdo atualizado que ele especificar e preencha o cache do aplicativo. Dessa forma, são necessários dois novos carregamentos de página para veicular o novo conteúdo aos usuários: um para que o servidor faça o download do novo cache do aplicativo e outro para atualizar o conteúdo da página.

A boa notícia: você não precisa ter esse trabalho. Para atualizar os usuários para a versão mais recente de seu site, defina um ouvinte para monitorar o evento updateready no carregamento da página:

// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {

  window.applicationCache.addEventListener('updateready', function(e) {
    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
      // Browser downloaded a new app cache.
      // Swap it in and reload the page to get the new hotness.
      window.applicationCache.swapCache();
      if (confirm('A new version of this site is available. Load it?')) {
        window.location.reload();
      }
    } else {
      // Manifest didn't changed. Nothing new to server.
    }
  }, false);

}, false);

Eventos do AppCache

Como esperado, eventos adicionais são exibidos para monitorar o estado do cache. O navegador dispara eventos para itens como andamento do download, atualização do cache do aplicativo e condições de erro. O snippet a seguir configura ouvintes de eventos para cada tipo de evento de cache.

function handleCacheEvent(e) {
  //...
}

function handleCacheError(e) {
  alert('Error: Cache failed to update!');
};

// Fired after the first cache of the manifest.
appCache.addEventListener('cached', handleCacheEvent, false);

// Checking for an update. Always the first event fired in the sequence.
appCache.addEventListener('checking', handleCacheEvent, false);

// An update was found. The browser is fetching resources.
appCache.addEventListener('downloading', handleCacheEvent, false);

// The manifest returns 404 or 410, the download failed,
// or the manifest changed while the download was in progress.
appCache.addEventListener('error', handleCacheError, false);

// Fired after the first download of the manifest.
appCache.addEventListener('noupdate', handleCacheEvent, false);

// Fired if the manifest file returns a 404 or 410.
// This results in the application cache being deleted.
appCache.addEventListener('obsolete', handleCacheEvent, false);

// Fired for each resource listed in the manifest as it is being fetched.
appCache.addEventListener('progress', handleCacheEvent, false);

// Fired when the manifest resources have been newly redownloaded.
appCache.addEventListener('updateready', handleCacheEvent, false);

Se houver falhas no download de um arquivo de manifesto ou de um recurso especificado nele, todo o processo de atualização do cache falhará. O navegador continuará a usar o cache do aplicativo antigo caso ocorra uma dessas falhas.

Referências

Comments

0