Dominando HTML5 offline com AppCache
Sérgio Lopes
Offline?
Application Cache API
Arquivo manifesto
CACHE MANIFEST
/index.html
/imagens/logo.png
/javascript/script.js
/css/estilo.css
Linkar pro manifesto
<html manifest="demo.appcache">
Pronto! Fácil!
Rá!
Não é fácil!
Mime-type
text/cache-manifest
Meu site bonitão!
Meu site bonitão!
Rá!
Fora do manifesto? Não funciona!
Seção network
CACHE MANIFEST
/index.html
/imagens/logo.png
/javascript/script.js
/css/estilo.css
NETWORK:
http://www.google-analytics.com/ga.js
Seção network
CACHE MANIFEST
/index.html
/imagens/logo.png
/javascript/script.js
/css/estilo.css
NETWORK:
*
E quando estou offline?
Usuários:
Usuários:
Seção fallback
CACHE MANIFEST
/index.html
/imagens/logo.png
/javascript/script.js
/css/estilo.css
NETWORK:
*
FALLBACK:
/img/avatares/ /img/avatar-generico.png
Usuários:
Detalhes do manifesto
Página com manifesto
CACHE MANIFEST
/index.html
/imagens/logo.png
/javascript/script.js
/css/estilo.css
<!-- index.html -->
<html manifest="demo.appcache">
Página com manifesto
CACHE MANIFEST
/imagens/logo.png
/javascript/script.js
/css/estilo.css
<!-- index.html -->
<html manifest="demo.appcache">
Rá!
Query strings
CACHE MANIFEST
/imagens/logo.png
/javascript/script.js
/css/estilo.css
<img src="/imagens/logo.png?malandragem">
Rá!
404 ou 500? Cache todo é descartado!
Rá!
Cache-Control: no-store
API JavaScript
window.applicationCache
Feature test
if ('applicationCache' in window) {
// AppCache disponível!
} else {
// sinto muito!
}
Eventos JS
applicationCache.onchecking = function(){ /* ... */ }
Eventos JS
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.ondownloading = function(){ /* ... */ }
Eventos JS
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.ondownloading = function(){ /* ... */ }
applicationCache.onprogress = function(){ /* ... */ }
Progresso
applicationCache.onprogress = function(e){
log('Baixamos já ' + e.loaded + ' de ' + e.total);
}
Eventos JS
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.ondownloading = function(){ /* ... */ }
applicationCache.onprogress = function(){ /* ... */ }
applicationCache.oncached = function(){ /* ... */ }
Eventos JS
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.ondownloading = function(){ /* ... */ }
applicationCache.onprogress = function(){ /* ... */ }
applicationCache.oncached = function(){ /* ... */ }
applicationCache.onerror = function(){ /* ... */ }
Status
applicationCache.status
// 2 == CHECKING
// 3 == DOWNLOADING
// ...
E depois?
Servido sempre do cache!
Rá!
Atualiza só quando o manifesto muda
CACHE MANIFEST
/imagens/logo.png
/css/estilo.css
/javascript/script.js
CACHE MANIFEST
/imagens/logo.png
/css/estilo.css
/javascript/script.js
/javascript/jquery.js
Queria mudar o próprio arquivo
CACHE MANIFEST
/imagens/logo.png
/css/estilo.css
/javascript/script.js
/javascript/jquery.js
CACHE MANIFEST
/imagens/logo.png
/css/estilo.css
/javascript/script.js?v2
/javascript/jquery.js
CACHE MANIFEST
/imagens/logo.png
/css/estilo.css
/javascript/script-v2.js
/javascript/jquery.js
CACHE MANIFEST
# Versão 2
/index.html
/imagens/logo.png
/css/estilo.css
/javascript/script.js
/javascript/jquery.js
Eventos JS - Update
applicationCache.onchecking = function(){ /* ... */ }
Eventos JS - Update
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.onnoupdate = function(){ /* ... */ }
Eventos JS - Update
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.onnoupdate = function(){ /* ... */ }
// ou
applicationCache.ondownloading = function(){ /* ... */ }
applicationCache.onprogress = function(){ /* ... */ }
Eventos JS - Update
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.onnoupdate = function(){ /* ... */ }
// ou
applicationCache.ondownloading = function(){ /* ... */ }
applicationCache.onprogress = function(){ /* ... */ }
applicationCache.onupdateready = function(){ /* ... */ }
Não tá atualizando!
Rá!
Lembre do HTTP cache normal
Não cacheie o manifesto!
Atualizou? Não vi!
Rá!
Reload
applicationCache.onupdateready = function() {
if (confirm('Tem atualização. Recarregar?')) {
location.reload();
}
}
Swap Cache
applicationCache.onupdateready = function() {
applicationCache.swapCache();
}
Swap Cache
applicationCache.onupdateready = function() {
ajax('/teste.html', ...); // versão velha
applicationCache.swapCache();
ajax('/teste.html', ...); // versão nova!
}
Tem atualização?
Update
applicationCache.update();
Update
setTimeout(function(){
applicationCache.update();
}, 1000 * 60 * 60);
Como apagar?
Eventos JS - Remove
applicationCache.onchecking = function(){ /* ... */ }
applicationCache.onobsolete = function(){ /* ... */ }
O usuário não controla nada!
Rá!
Controle na mão do usuário
instalar.html
<html manifest="apostila.appcache"></html>
CACHE MANIFEST
/apostila/
Disparar instalação
botaoInstalar.onclick = function(){
document.body.innerHTML
+= '<iframe src="instalar.html"></iframe>';
}
E o update?
Abort
applicationCache.onchecking = function() {
applicationCache.abort();
}
Rá!
Impedir update automático
Request do manifesto
Um pouco de PHP
<?php
if ($_COOKIE['offline'] == 'noupdate') {
// falo que manifesto não mudou
} else {
// mando o manifesto novo
}
?>
Enviando o manifesto
<?php
if ($_COOKIE['offline'] == 'noupdate') {
// falo que manifesto não mudou
} else {
include('apostila.appcache');
}
?>
304 Not Modified
304 no servidor
<?php
if ($_COOKIE['offline'] == 'noupdate') {
header('HTTP/1.1 304 Not Modified');
} else {
include('apostila.appcache');
}
?>
Cookie no browser
// após a instalação inicial
applicationCache.oncached = function() {
document.cookie = 'offline=noupdate';
}
Quero atualizar!
botaoAtualizar.onclick = function() {
document.cookie = '';
// recarrega o iframe de instalação
}
Tem atualização?
versao.txt
10345675
versao.txt
10345675
CACHE MANIFEST
/apostila/
/versao.txt
# ....
Qual versão tenho?
ajax('/versao.txt', function(versao){
alert('Versão instalada: ' + versao)
})
Qual a última versão?
ajax('/versao.txt?ultima', function(ultima){
alert('Versão no servidor: ' + ultima)
})
Aviso de update
ajax('/versao.txt', function(versao){
ajax('/versao.txt?ultima', function(ultima){
if (versao !== ultima) {
// avisa o usuario que tem nova versao
// e dá opção de instalação
}
})
})
Não quero mais!
404 no servidor
<?php
if ($_COOKIE['offline'] == 'noupdate') {
header('HTTP/1.1 304 Not Modified');
} elseif ($_COOKIE['offline'] == 'remove') {
header('HTTP/1.1 404 Not Found');
} else {
include('apostila.appcache');
}
?>
Cookie no browser
// quero atualizar!
botaoRemover.onclick = function() {
document.cookie = 'offline=remove';
// recarrega o iframe de instalação
}
<?php
header("Vary: Cookie");
header("Expires: Thu, 01 Jan 1970 00:00:00 GMT");
if ($_COOKIE['offline'] == 'noupdate') {
header('HTTP/1.1 304 Not Modified');
} elseif ($_COOKIE['offline'] == 'remove') {
header('HTTP/1.1 404 Not Found');
} else {
header('Content-Type: text/cache-manifest');
include('apostila.appcache');
}
?>