Zeig mir Deine
Offline Seite
(Progessive Web App)
Twitter: @mittlmedien
Email: info@mittl-medien.de
Über mich
-
Robert Mittl
-
Stuttgart, Germany
-
selbständig seit 2012
-
Webentwickler
-
JUG Stuttgart
Twitter: @mittlmedien
Email: info@mittl-medien.de
Internet
-
Geschwindigkeit oft langsam 😪
-
schlechte Verbindung
Webseiten offline speichern
-
nur wenige werden das tun
-
zumindest nicht manuell
-
wir werden das zusammen tun 👏
Progressive Web App
-
als Ansatz zur Lösung
- ist eine Möglichkeit Webseiten offline verfügbar zu machen und eine native App funktionell nachzubilden, die installiert werden können
Vorteile - man könnte
-
bei einer Webseite mit den Kontaktdaten anzeigen, wenn offline
-
oder speichere eine bereits besuchte Webseite, so dass Sie erneut ohne Internetverbindung aufrufbar wäre
Zauberwort Serviceworker
regelt unser Vorhaben
Server
Serviceworker
Browser
Theorie am Praxisbeispiel
Grundlage eine Joomla Webseite
so "hübsch" wie wir Sie kennen 🤭
Voraussetzungen👆
-
Browserunterstützung (jeder Browser ist auf unterschiedlichem Stand der Implementierung)
-
SSL (oder localhost)
Serviceworker Lifecylcle 🔄
-> Register
-> Download
-> Installation
-> Warten
-> Aktivierung
Register Serviceworker im Template
window.addEventListener('load', async() => {
if("serviceWorker" in window.navigator) {
try {
await window.navigator.serviceWorker.register('/serviceworker.js')
} catch (error) {
console.error(error)
}
}
})
fetch Event in serviceworker.js
self.addEventListener('fetch', e => {
console.log(e.request)
})
Meine erste Offline Nachricht - Promise
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'
}
})
}
)
)
})
Meine erste Offline Nachricht - Async Await
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)
)
})
💪 Offline Nachricht 👏
-> Offline Seite 🤔
Zauberwort Cache
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
Cache Files
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()
)
})
Cached Files
laden aus dem Cache
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
serviceworker.js darf selbst nicht gecached werden
<IfModule mod_expires.c>
<FilesMatch "serviceworker.js">
ExpiresDefault "access plus 0 seconds"
</FilesMatch>
</IfModule>”
Cache säubern
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
Offline Page
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
Cache
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
Cache von Seiten
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')
}
}
Offline Seite ergänzen
<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>
Cache säubern auf Anzahl der Items beschränken - Funktion
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)
}
}
Cache säubern auf Anzahl der Items beschränken - Message Event
self.addEventListener('message', messageEvent => {
if (messageEvent.data === 'clean up caches') {
trimCacheAsync(imageCacheName, 50)
trimCacheAsync(pagesCacheName, 30)
}
})
Cache säubern auf Anzahl der Items beschränken - beim registrieren
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/serviceworker.js')
if (navigator.serviceWorker.controller) {
window.addEventListener('load', _ => {
navigator.serviceWorker.controller.postMessage('clean up caches')
})
}
}
PWA Manifest
<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"
},
{
Weitere 😎 Features:
-
Push Notifications
-
Background Sync
Push Notifications
-
Background Push
-
Active Push
☝️ Achtung kein Support in iOS oder Safari
Push Notifications
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'
})
}
})
Push Notifications
mögliche Beispiele:
- bei Aktualisierung des Serviceworkers (Updatebenachrichtigung)
-
Bei Auslösung bestimmter Ereignisse
-
Versand über einen Dienst der dann den Serviceworker anspricht (Firebase Cloud Messaging, Cleverpush, OneSignal)
Beispiele
Trivago
oder schaut wieviel Service Worker bei Euch im Browser bereits installiert sind
Tools für PWA
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”
Fragen?
Twitter: @mittlmedien
Email: info@mittl-medien.de
Danke 👏
Quellen
Buch: Jeremy Keith. “Going Offline”
Peter Kröner: PWA Workshop
MDN: Service Worker
Zeig mir Deinen Offline Seite! (Progressive Web App)
By Robert Mittl
Zeig mir Deinen Offline Seite! (Progressive Web App)
- 1,646