ARMAZENAMENTO OFFLINE
APIS PARA PROGRESSIVE WEB APPS
@eduardojmatos eduardomatos.me
OI, EU SOU O EDU
a maior plataforma de contratação
de serviços do Brasil
VAMOS FALAR SOBRE INTERNET?
www.teleco.com.br/3G_cobertura.asp
http://www.teleco.com.br/4G_cobertura.asp
NETFLIX ISP SPEED INDEX
53% dos consumidores perdem a paciência e desistem de
acessar o site após apenas três segundos. A expectativa do
consumidor (afirmação feita por 79% dos entrevistados) é de
encontrar as informações que deseja instantaneamente.
http://exame.abril.com.br/marketing/quer-posicionar-bem-a-marca-entao-de-um-jeito-no-seu-site-movel/
Mais da metade deles (56%) preferem comprar em sites
móveis ou apps que permitem salvar preferências—
entre consumidores entre 18 e 34 anos, esse número
sobe para 64%.
http://exame.abril.com.br/marketing/quer-posicionar-bem-a-marca-entao-de-um-jeito-no-seu-site-movel/
68% consideram 6 segundos um tempo de carregamento
aceitável, mas que até 3 segundos era o “ponto ideal”.
https://www.tecmundo.com.br/apps/81635-paciencia-brasileiro-apps-lentos-dura-somente-3-segundos.htm
3 segundos era o “ponto ideal”
COMO ISSO DEVERIA
IMPACTAR NOSSO
DESENVOLVIMENTO?
PWA
progressive web apps
Progressive Web Apps é a experiência do
usuário ao alcance da web sendo
Confiável, Rápida e Engajante.
TÁ, E COMO FAZ?
CONFIÁVEL
• Carrega quase que instantaneamente;
• Independe das condições de conexão com a internet;
• Honestidade com relação à conectividade.
APPLICATION CACHE
CACHE DE ARQUIVOS NO BROWSER
<html manifest="manifest.appcache">
CACHE MANIFEST
# version 1
CACHE:
http://jsexperience2017.imasters.com.br/images/logo.jpg
http://jsexperience2017.imasters.com.br/palestrantes/01.jpg
http://jsexperience2017.imasters.com.br/palestrantes/02.jpg
NETWORK:
https://facebook.com/sdk.js
FALLBACK:
index.html offline.html
SERVICE WORKERS
e
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/js/sw.js')
.then(reg => {
if(reg.installing) {
console.log('Service worker installing');
} else if(reg.waiting) {
console.log('Service worker installed');
} else if(reg.active) {
console.log('Service worker active');
}
}).catch(error => {
console.error('Registration failed with', error);
});
};
navigator.serviceWorker.register('/js/sw.js')
.then(reg => {
reg.addEventListener('updatefound', () => {
const newServiceWorker = reg.installing;
newServiceWorker.addEventListener('statechange', () => {
if (newServiceWorker.state == 'activated') {
console.info('SW is active!');
}
})
});
})
.catch(error => error)
CACHE API
CACHE PROGRAMÁTICO
DE ARQUIVOS NO BROWSER
// /js/sw.js
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => {
return cache.addAll([
'/index.html',
'/js/app.js',
'/css/styles.css',
'/images/avatar/profile/01.jpg'
]);
})
);
});
VERSIONAMENTO DE CACHE
ANTES DE SERVICE
WORKERS
DEPOIS DE SERVICE
WORKERS
INTERCEPTAÇÃO DE REQUEST
QUALQUER REQUEST PASSA
PELO EVENTO DE FETCH
self.addEventListener('fetch', event => {
event.respondWith(caches.open('v1').then(cache => {
return cache.match(event.request)
.then(cached => {
if (cached) return cached;
throw new Error('No cache founded');
})
.catch(error => {
return fetch(event.request)
.then(response => {
cache.put(event.request, response.clone());
return response;
}).catch(error => caches.match('offline.html'));
})
})
);
});
RÁPIDA
• JavaScript performático;
• Interações fluídas e com sensação de velocidade;
• Cache First.
document.write(
'<script src="https://js.com/script.js"></script>'
);
JS PERFORMÁTICO
https://developers.google.com/web/updates/2016/08/removing-document-write
NOT
• 38% de redução em conexão 2G;
• 21% de redução até o first paint;
DOMAttrModified
DOMAttributeNameChanged
DOMCharacterDataModified
DOMElementNameChanged
DOMNodeInserted
DOMNodeInsertedIntoDocument
DOMNodeRemoved
DOMNodeRemovedFromDocument
DOMSubtreeModified
JS PERFORMÁTICO
https://developers.google.com/web/tools/lighthouse/audits/mutation-events
NOT
• Performance baixa!
• Deprecated events!
document.addEventListener('DOMContentLoaded', (event) => {
navigator.geolocation.getCurrentPosition(success, error);
Notification.requestPermission((result) => {
resolve(result);
});
});
EVITE
JS PERFORMÁTICO
https://developers.google.com/web/fundamentals/engage-and-retain/push-notifications/display-a-notification
• Apenas use esses recursos quando o usuário precisar;
• Exiba notificações somente quando necessário;
document.addEventListener(
'touchstart',
onTouchStart,
{ passive: true });
INTERAÇÕES FLUÍDAS
https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
INTERAÇÕES FLUÍDAS
https://www.youtube.com/watch?v=65VMej8n23A
CACHE FIRST
self.addEventListener('fetch', event => {
event.respondWith(caches.open('v1').then(cache => {
return cache.match(event.request)
.then(cached => {
if (cached) return cached;
//…
CACHE FIRST
npm i sw-precache -g
sw-precache
—config=precache-config.js
--verbose
USE SW-PRECACHE
module.exports = {
staticFileGlobs: [
'app/css/**.css',
'app/**.html',
'app/images/**.*',
'app/js/**.js'
],
stripPrefix: 'app/',
runtimeCaching: [{
handler: 'cacheFirst'
}]
};
ENGAJANTE
• Web App Manifest;
• Push Notifications;
WEB APP MANIFEST
<link rel="manifest" href="/manifest.json">
{
"name": "Offline App Presentation",
"short_name": "OfflineAPp",
"start_url": ".",
"display": "standalone",
"background_color": "#fff",
"description": "Testing our Offline App Presentation",
"icons": [{
"src": “images/touch/icon-168.png",
"sizes": "168x168",
"type": "image/png"
}],
"related_applications": [{
"platform": "web"
}, {
"platform": "play",
"url": "https://play.google.com/store/apps/details?id=offapp"
}]
}
window.addEventListener('beforeinstallprompt', function(e) {
e.userChoice.then(function(choiceResult) {
console.log(choiceResult.outcome);
if(choiceResult.outcome == 'dismissed') {
console.log('Sad, but user cancelled home screen install');
}
else {
console.log('Yes! User added to home screen');
}
});
});
WEB APP MANIFEST
• Ter um web app manifest com:
• short_name (usado na home screen);
• name (usado no banner de instalação);
• um ícone png de 144x144;
• “start_url” que o app é carregado.
• Ter um service worker registrado em seu site;
• Servido com HTTPS (pra usar Service Worker isso já é um requisito);
• Foi visitado pelo menos duas vezes, em pelo menos 5 minutos entre as visitas
CRITÉRIOS PRA EXIBIÇÃO DO BANNER DE INSTALAÇÃO DO WEB APP
NOTIFICAÇÕES
function askPermission() {
return new Promise((resolve, reject) => {
const permissionResult = Notification.requestPermission((result) => {
resolve(result);
});
if (permissionResult) {
permissionResult.then(resolve, reject);
}
})
.then((permissionResult) => {
if (permissionResult !== 'granted') {
throw new Error('Fail on granted permission.');
}
});
}
function subscribeUserToPush() {
return getSWRegistration()
.then((registration) => {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-
SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'
)
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then((pushSubscription) => {
console.log('Received: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
}
self.addEventListener('push', function(event) {
const promiseChain = self.registration.showNotification(
'Hello, World.', {
body: 'notification test!',
icon: '/images/icon-512x512.png',
image: '/images/body-notification.jpg',
vibrate: [
500, 110, 500, 110, 450, 110, 200, 110, 170, 40, 450,
110, 200, 110, 170, 40, 500
],
sound: 'starWarsPing.mp3'
}
);
event.waitUntil(promiseChain);
});
NOTIFICAÇÕES
https://tests.peter.sh/notification-generator
https://web-push-book.gauntface.com/demos/notification-examples/
TESTE A NOTA DO SEU PWA
npm install -g lighthouse
lighthouse https://www.google.com
TESTE A NOTA DO SEU PWA
RESUMINDO…
JAVASCRIPT ❤ PWA
PWA É
PROGRESSIVO
USE E ABUSE PENSANDO QUE FOI FEITO PRA WEB
TESTE SE AS API’S ESTÃO DISPONÍVEIS NO BROWSER
JABÁ
http://bit.ly/design-patterns-2
JABÁ 2
http://bit.ly/pwa-js-2
@eduardojmatos
http://eduardomatos.me
eduardoj.matos@gmail.com
OBRIGADO ;)

Armazenamentos offline-ap is-para-pwa