Twitter: @mittlmedien
Email: info@mittl-medien.de
Robert Mittl
Stuttgart, Germany
selbständig seit 2012
Webentwickler
JUG Stuttgart
Twitter: @mittlmedien
Email: info@mittl-medien.de
Geschwindigkeit oft langsam 😪
schlechte Verbindung
nur wenige werden das tun
zumindest nicht manuell
wir werden das zusammen tun 👏
als Ansatz zur Lösung
bei einer Webseite mit den Kontaktdaten anzeigen, wenn offline
oder speichere eine bereits besuchte Webseite, so dass Sie erneut ohne Internetverbindung aufrufbar wäre
regelt unser Vorhaben
Server
Serviceworker
Browser
so "hübsch" wie wir Sie kennen 🤭
Browserunterstützung (jeder Browser ist auf unterschiedlichem Stand der Implementierung)
SSL (oder localhost)
window.addEventListener('load', async() => {
if("serviceWorker" in window.navigator) {
try {
await window.navigator.serviceWorker.register('/serviceworker.js')
} catch (error) {
console.error(error)
}
}
})
self.addEventListener('fetch', e => {
console.log(e.request)
})
self.addEventListener('fetch', e => {
const request = e.request
e.respondWith(
fetch(request)
.then(responseFromFetch => {
// console.log(responseFromFetch)
return responseFromFetch
})
.catch(error => {
console.log(error)
return new Response('<h1>Meine Joomla Seite ist offline</h1>',
{
headers: {
'Content-type': 'text/html; charset=utf-8'
}
})
}
)
)
})
const fetchRequest = async (request) => {
try {
const responseFromFetch = await fetch(request)
return responseFromFetch
} catch (error) {
console.log(error)
return new Response('<h1>Meine Joomla Seite ist offline</h1>',
{
headers: {
'Content-type': 'text/html; charset=utf-8'
}
})
}
}
self.addEventListener('fetch', e => {
e.respondWith(
fetchRequest(e.request)
)
})
Versionierung und Festlegen des Cache
const version = 'V0.01'
const staticCacheName = version + 'staticfiles'
self.addEventListener('install', e => {
e.waitUntil(
)
})
Event.waitUntil() -> wartet mit der Installation des Service Workers, bis der Code innerhalb ausgeführt wird
const cacheFiles = async () => {
const cache = await caches.open(staticCacheName)
const files = await cache.addAll([
'/templates/protostar/css/template.css',
'/media/jui/js/jquery.min.js',
'/media/jui/js/jquery-noconflict.js',
'/media/jui/js/jquery-migrate.min.js',
'/media/system/js/caption.js',
'/media/jui/js/bootstrap.min.js',
'/templates/protostar/js/template.js',
'/media/system/js/core.js',
'/media/system/js/keepalive.js'
])
return files
}
self.addEventListener('install', e => {
e.waitUntil(
cacheFiles()
)
})
const cacheMatch = async (request) => {
try {
const url = request.url
const cleanUrl = url.split('?')
const cacheMatch = await caches.match(cleanUrl[0])
if (cacheMatch) {
console.log('cacheMatch', cacheMatch)
return cacheMatch
}
return fetch(request)
} catch (error) {
console.log(error)
}
}
self.addEventListener('fetch', e => {
e.respondWith(
cacheMatch(e.request)
)
})
url.split() trennen, wegen Hash
<IfModule mod_expires.c>
<FilesMatch "serviceworker.js">
ExpiresDefault "access plus 0 seconds"
</FilesMatch>
</IfModule>”
addEventListener('activate', e => {
e.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName != staticCacheName) {
return caches.delete(cacheName)
}
})
)
})
.then(() => {
return clients.claim()
})
)
})
clients.claim sorgt dafür, dass es gleich ausgführt wird, ohne dass ein Browser Refresh nötig ist
const files = await cache.addAll([
'...,
'/templates/protostar/offline.html',
...
])
im statischen Cache hinzufügen und im Template anlegen
try{
//....
const fetchRequest = await fetch(request)
return fetchRequest
} catch (error) {
return caches.match('/templates/protostar/offline.html')
}
und wenn Fehler Request --> offline.html
if (request.headers.get('Accept').includes('text/html')) {
if (request.url.endsWith('/joomla-doc')
/....
}
if (request.url.match(/\.(jpe?g|png|gif|svg)$/)) {
if (request.url.includes('piwik')) return
}
Bilder, HTML und nach URL filtern
const responseFrom = async (request) => {
try {
const url = request.url
const cleanUrl = url.split('?')
const cacheMatch = await caches.match(cleanUrl[0])
if (cacheMatch) {
return cacheMatch
} else {
const fetchRequest = await fetch(request)
if (fetchRequest.ok) {
const cache = await self.caches.open(pageCacheName)
cache.put(request, fetchRequest.clone())
}
return fetchRequest
}
} catch (error) {
return caches.match('/templates/protostar/offline.html')
}
}
<p>Folgende Seiten kannst Du offline besuchen:</p>
<ul id="history"></ul>
<script>
caches.open('pages')
.then(pagesCache => {
pagesCache.keys()
.then(keys => {
let markup = ''
keys.forEach(request => {
markup += `<li><a href="${request.url}">${request.url}</a></li>`
})
document.querySelector('#history').innerHTML = markup
})
})
</script>
const trimCacheAsync = async (cacheName, maxItems) => {
try {
const responseFromCache = await caches.open(cacheName)
const cacheKeys = await responseFromCache.keys()
const items = await cacheKeys
if (items.length > maxItems) {
cache.delete(items[0])
.then(
trimCacheAsync(cacheName, maxItems)
)
}
} catch (error) {
//console.log('error is', error)
}
}
self.addEventListener('message', messageEvent => {
if (messageEvent.data === 'clean up caches') {
trimCacheAsync(imageCacheName, 50)
trimCacheAsync(pagesCacheName, 30)
}
})
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/serviceworker.js')
if (navigator.serviceWorker.controller) {
window.addEventListener('load', _ => {
navigator.serviceWorker.controller.postMessage('clean up caches')
})
}
}
<link rel="manifest" href="/manifest.json">
https://app-manifest.firebaseapp.com/
{
"lang": "de",
"name": "Joomla",
"short_name": "Joomla",
"description": "Meine Joomla PWA",
"theme_color": "#99ccff",
"background_color": "#99ccff",
"display": "standalone",
"Scope": "/",
"start_url": "/",
"icons": [
{
"src": "images/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
Push Notifications
Background Sync
Background Push
Active Push
☝️ Achtung kein Support in iOS oder Safari
pushButton.addEventListener('click', async () => {
const permission = await Notification.requestPermission()
if (permission === 'granted') {
const notifyObj = new Notification('Joomla Day Austria', {
body: 'Was für eine schöner Tag 😀',
image: './images/icons/icon-512x512.png',
icon: './images/icons/icon-152x152.png',
badge: './images/icons/icon-72x72.png'
})
}
})
mögliche Beispiele:
Bei Auslösung bestimmter Ereignisse
Versand über einen Dienst der dann den Serviceworker anspricht (Firebase Cloud Messaging, Cleverpush, OneSignal)
Trivago
oder schaut wieviel Service Worker bei Euch im Browser bereits installiert sind
Workbox von Google
zum testen:
Lighthouse in Chrome für Manifest
Google PWA Training:
https://developers.google.com/web/ilt/pwa/
Buch: Jeremy Keith. “Going Offline”
Twitter: @mittlmedien
Email: info@mittl-medien.de
Buch: Jeremy Keith. “Going Offline”
Peter Kröner: PWA Workshop
MDN: Service Worker