Offline first ist das neue mobile first - International PHP Conference

Die Seite wird erstellt Hugo Ahrens
 
WEITER LESEN
Offline first ist das neue mobile first - International PHP Conference
Offline first ist das neue mobile
 first
Offline first ist das neue mobile first - International PHP Conference
Über mich
Sebastian Springer

• JavaScript Entwickler
• aus München
• bei MaibornWolff
• https://github.com/sspringer82
• @basti_springer
• Techstack: Node & React
Offline first ist das neue mobile first - International PHP Conference
Server Client
Offline first ist das neue mobile first - International PHP Conference
Lass’ uns doch eine PWA
 machen
Offline first ist das neue mobile first - International PHP Conference
PWA
Progressive Web App

• Schnell und zuverlässig: Startet schnell und bleibt schnell
• Installierbar: Kann auf dem Zielsystem installiert werden
• Mobile & Desktop: Verhält sich auf dem Zielsystem wie eine native App
Offline first ist das neue mobile first - International PHP Conference
Offline first
Wie funktioniert die Applikation ohne Internet

• Offline ist kein Ausnahmezustand
• Benutzer können die Applikation (mit Einschränkungen) verwenden
• Es findet reguläre Interaktion statt
• Sobald die Verbindung wiederhergestellt ist, werden die Daten synchronisiert
Offline first ist das neue mobile first - International PHP Conference
Offline?
Es gibt nicht nur schwarz und weiß

• Keine Verbindung
• Sehr langsame Verbindung
• Keine Verbindung zum Server
Offline first ist das neue mobile first - International PHP Conference
Schnittstellen
Welche Browser-APIs benötigen wir?

• Web App Manifest
• Service Worker
• IndexedDB
Offline first ist das neue mobile first - International PHP Conference
Web App Manifest

 Helene Souza / pixelio.de
Offline first ist das neue mobile first - International PHP Conference
Web App Manifest

• JSON-Datei mit Metainformationen
• Wird über ein link-Tag eingebunden
• Enthält z.B.
 • Namen (kurz und lang)
 • Display - wie soll die Applikation angezeigt werden
 • Icons
Web App Manifest

 {
 "short_name": "Timetracker",
 "name": "Time tracking application",
 "icons": [
 {
 "src": "favicon.ico",
 "sizes": "64x64 32x32 24x24 16x16",
 "type": "image/x-icon"
 },…
 ],
 "start_url": ".",
 "display": "standalone",
 "theme_color": "#000000",
 "background_color": "#ffffff"
 }
Service Workers

 Herbert Käfer / pixelio.de
Service Worker
Hintergrundprozess im Browser

• Wird bei Bedarf aktiviert
• Features: Caching, Push Notifications, Background Sync
• Kein Zugriff auf das DOM
• Kommunikation über postMessage
• Persistenz: IndexedDB
Service Workers
 Frontend Applikation
Browser

 Service Worker

 HTTP

Server Backend
Service Workers
 HTTPS only (oder localhost)
Service Workers

 Installed/
Parsed Installing Activating Activated Redundant
 Waiting
Service Workers
• Parsed: initiales Einlesen des Scripts. SW kann registriert werden.

• Installing: SW wird installiert. Hier können Dateien gecached werden.

• Installed/Waiting: SW ist erfolgreich installiert, hat aber noch keine
 Kontrolle über die Applikation. Hier kann der Benutzer informiert werden.

• Activating: SW wird aktiviert. Hier können Caches aufgeräumt werden.

• Active: Der SW ist aktiv und kontrolliert die Applikation.

• Redundant: Installation oder Aktivierung ist fehlgeschlagen oder es gibt
 einen neueren SW.
Service Workers

Beim ersten Aufruf wird der SW zwar installiert aber noch
 nicht aktiviert.

 Achtung: Updates werden eventuell zeitverzögert
 angewendet.
Service Workers
Service Workers
 https://serviceworke.rs/
Service Workers
Workbox
JavaScript Library for adding offline support to web apps

• Basis für Create React App
• Plugin-Schnittstellen für Erweiterungen verfügbar
• Verschiedene Caching-Strategien
Workbox
 import {precacheAndRoute} from 'workbox-precaching';
 import {registerRoute} from 'workbox-routing';
 import {CacheFirst} from 'workbox-strategies';

 precacheAndRoute(self.__WB_MANIFEST);
 registerRoute(/^https:\/\/example.com\/assets/, new CacheFirst({
 cacheName: 'static-assets',
 matchOptions: {
 ignoreVary: true,
 },
 plugins: [new ExpirationPlugin({
 maxEntries: 500,
 maxAgeSeconds: 63072e3,
 purgeOnQuotaError: true,
 }), new CacheableResponsePlugin({
 statuses: [0, 200]
 })]
 }));
Workbox
Cache Strategien

• Stale-While-Revalidate: Cache First und paralleles Cache Update
• Cache First (Cache Falling Back to Network)
• Network First (Network Falling Back to Cache)
• Network Only Cache First

• Cache Only

 https://developers.google.com/web/tools/workbox/modules/workbox-strategies
IndexedDB

 Rainer Sturm / pixelio.de
IndexedDB

Auf die IndexedDB kann aus dem Haupt- und den Worker-Prozessen zugegriffen werden, um
 Daten zu speichern und zu lesen.

Statt Daten direkt zum Server zu senden oder sie von dort zu lesen, kann die IndexedDB als
 Zwischenschritt verwendet werden.
IndexedDB
Dexie.js
const db = new Dexie(‘TodoList');

db.version(1).stores({
 todos: '++id, title'
});

await db.todos
 .where('title')
 .equals('Go to work')
 .toArray();

await db.todos.add({
 title: 'Go to sleep',
 status: 'open',
});
Das Standardverhalten
Das Standardverhalten
Die out-of-the-box PWA von React

• Vorkonfigurierter Service Worker mit “cache-first”
• Service Worker ist standardmäßig deaktiviert
• Service Worker kann nur im production Build aktiviert werden
• Template für Manifestdatei
Das Standardverhalten
Die out-of-the-box PWA von React

• src/index.tsx: serviceWorker.unregister(); => serviceWorker.register();
• npm run build
• npx http-server build
Das Standardverhalten
Installierbar
API Calls
API Calls
Was ist mit dynamischen Anfragen?

• Anfragen an eine Web-API
 • Schreibende Zugriffe
 • Lesende Zugriffe
• Daten müssen für den offline-Betrieb zwischengespeichert werden
• Anfragen müssen offline abgefangen werden
• Sobald die Verbindung wiederhergestellt ist, müssen die Abfragen abgespielt
 werden
2 Lösungsmöglichkeiten

• Im Serviceworker

• In der Applikation
Offlinefähigkeit im Service
Worker
Offlinefähigkeit im Service Worker

• Der Serviceworker fängt die Anfragen ab
 • Sie werden entweder an den Server weitergeleitet
 • Oder direkt beantwortet
• Die Steuerung der Offlinefähigkeit erfolgt im JavaScript-Code des Service
 Workers

• Die IndexedDB wird als Persistenzschicht verwendet
Offlinefähigkeit im Service Worker

 self.addEventListener('fetch', function(event) {
 if (event.request.url.includes('/items')) {
 if (event.request.method === 'GET') {
 event.respondWith(async () => {
 const items = await itemModel.getAll();
 await itemModel.store(items);
 fetchHelper.createResponse(items);
 });
 } else if(event.request.method === 'POST') {
 ...
 }
 }
 });
Offlinefähigkeit in der Applikation
Offlinefähigkeit in der Applikation

• Die Applikation entscheidet, ob eine Anfrage an den Server gesendet wird
• Alle lesenden Anfragen werden automatisch in der IndexedDB
 zwischengespeichert

• Ist der Server nicht erreichbar, wird lokal zwischengespeichert
• Sobald der Server verfügbar ist, wird synchronisiert
• Die Applikation ist Offline-First implementiert und jederzeit offline arbeitsfähig
Offlinefähigkeit in der Applikation
async function loadLogs() {
 try {
 const response = await axios.get('http://localhost:3001/logs');
 if (!response) {
 throw new Error(NETWORK_ERROR);
 }
 db.table('items').clear();
 db.table('items').bulkAdd(response.data);

 setLogs(data);
 } catch (e) {
 if (isNetworkError(e)) {
 const data = await db.table('items').toArray();
 setLogs(data);
 } else {
 throw new Error('Whoops ');
 }
 }
}
And the winner is…
And the winner is…
Vergleich der beiden Ansätze

• Offlinefähigkeit im Service Worker
 • Die Applikation weiß nichts von Offlinefähigkeit
 • Es existiert eine zweite (Schatten-) Applikation
• Offlinefähigkeit in der Applikation
 • Die Applikation ist komplexer
 • Die Offlinefähigkeit kann innerhalb der Applikation getestet werden
 • Der Serviceworker übernimmt nur statisches Caching
Persistenz
Serverseitig
Umgang mit Datensynchronisierung

• Was passiert bei neuen Datensätze, die offline erzeugt wurden?
• Wie können Konflikte aufgelöst werden?
Neue Datensätze

• Das Frontend vergibt im Offline-Modus temporäre IDs für neue Datensätze
• Änderungen und Löschungen werden im Offline-Modus verfolgt
 • Änderungen anwenden und nur ein Objekt synchronisieren
 • Änderungen als Events serverseitig abspielen (Eventsourcing)
• Werden die Daten zum Server gesendet, wird die temporäre ID ersetzt
Neue Datensätze

Client
 New Change Change
 Sync
 ID: c1 ID: c1 ID: c1

No Connection Connection

Server
 New Change Change

 ID: 42 ID: 42 ID: 42
Konflikte
Konflikte

• Im Multiuser-Betrieb versuchen mehrere Benutzer schreibend auf eine
 Ressource zuzugreifen

• Ist mindestens ein Benutzer offline, entsteht ein potenzieller Konflikt
• Konflikte müssen aufgelöst werden, um Inkonsistenzen zu vermeiden
Strategien zur Konfliktlösung

• Änderungen mit Konflikten abweisen (first write wins)
• Änderungen überschreiben (last write wins)
• Automatisches zusammenführen (unterschiedliche Felder werden
 überschrieben)

• Manuelles zusammenführen: Der Benutzer muss den Konflikt manuell
 auflösen
Noch eine Ebene weiter…
Zentrales
 Statemanagement
Server

 Async Middleware Action

Action Dispatcher Store View
Fragen?
…dann wäre jetzt ein guter Zeitpunkt

 Rainer Sturm / pixelio.de
Kontakt

 Sebastian Springer
 sebastian.springer@maibornwolff.de

 MaibornWolff GmbH
 Theresienhöhe 13
 80339 München

 @basti_springer

 https://github.com/sspringer82
Sie können auch lesen