Ta strona używa cookies
sprawdź politykę prywatności

Rozumiem

Progressive Web App. Push Notification i update Cache.

5/3/2019
Bartek Cis

Push Notification to niesamowita właściwość Progressive Web Apps czyli aplikacji webowych które działają offline. Dowiedz się jak jej używać.

Czego się dzisiaj dowiesz?

Na początku optymalizuje mechanizm cache w Service Workerze stworzonym w poprzednim wpisie. Opiszę różne aspekty z tym związane i jak sobie z nimi poradzić.

Następnie wzbogacę Service Worker/a o możliwość Push Notification oraz pokaże jak SW może się komunikować z głównym wątkiem JS i wzajemnie wymieniać informacje.

Zanim zaczniesz

Wszystko o czym tu pisze bazuje na poprzednich wpisach z tej serii. Zapoznaj się najpierw z nimi a wtedy to co jest opisane tutaj będzie super łatwe 🙂

Nie sugeruj się specjalnie słowem WordPress bo w kontekście dzisiejszego wpisu w zasadzie nie ma żadnego znaczenia z jaką technologią pracujesz. PWA działa wszędzie podobnie!

Plan działania

  1. Więcej o cache
  2. postMessage() czyli komunikacja JS z SW
  3. Push Notification

Seria Progressive Web App

Opisuje tutaj czym jest PWA i  jak stworzyć ją na WordPressie. Kolejne artykuły będą na bieżąco uzupełniane:

  1. Progressive Web App na WordPress – Teoria, Środowisko i Lighthouse
  2. Progressive Web App. Tworzę manifest.json i Service Worker.
  3. Progressive Web App. Push Notification i update Cache.

Service Worker Cache

W poprzednim wpisie zdefiniowałem cache przy instalacji SW w ten sposób:

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open('v1').then(cache => {
            return cache.addAll([
                '/wp-content/themes/bedekodzic/index.php',
                '/wp-content/themes/bedekodzic/style.css'
            ]);
        }, error => {
            console.log(`Installation failed with error: ${error}`);
        })
    );
});

Czyli dodałem do cache 2 pliki tzn. index.php, style.css

Service Worker fetch

W teorii przy kroku   fetch  SW powinien sprawdzić które zasoby są w cache a następnie je pobrać z cache by potem pobrać resztę zasobów z sieci. Więc bardzo ważne jest co zdefiniujemy w cache.

Jednak definiując mój fetch użyłem tego kodu:

if (cachedResponse) {
    event.waitUntil(cache.add(event.request));
    return cachedResponse;
}

Znaczy to tyle, że jeżeli dany zasób nie jest zdefiniowany w cache to go po prostu dodaj 🙂 W ten sposób wszystko wrzucamy do cache SW i po sprawie. Nie trzeba się brandzlować przy definiowaniu cache.

Ale czasem możesz chcieć cache’ować konkretne zasoby i nic więcej. Wtedy zdefiniujesz   fetch  inaczej ale należy też zmienić definicję cache przy instalacji SW…

Tablica zasobów

Zamiast wpisywać każdy plik z osobna bezpośrednio przy instalacji SW to stwórz tablicę z zasobami a następnie użyj jej przy instalacji:

const cacheName = 'v1';
const filesToCache = [
    '/wp-content/themes/bedekodzic/index.php',
    '/wp-content/themes/bedekodzic/main.bundle.js',
    '/wp-content/themes/bedekodzic/style.css'
];

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(cacheName).then(cache => {
            return cache.addAll(filesToCache);
        }, error => {
            console.log(`Installation failed with error: ${error}`);
        })
    );
});

Teraz to wygląda bardziej przejrzyście 🙂

Większa ilość zasobów w cache

Nie zmienia to faktu, że jeżeli mamy dużo plików lub grafik które mogą być dodatkowo ciągle uzupełniane to manualne dodawanie ich do tablicy to jakiś dramat…

Niestety z poziomu Front Endu nie mamy bezpośredniego dostępu do zasobów serwera. Nie można tak po prostu dodać * lub RegExa, np.   /wp-content/themes/bedekodzic/*   i dodać do cache wszystko co jest na serwerze… Potrzeba jest pomoc Back-Endu.

PHP

Jako, że WordPress bazuje właśnie na tym to przeanalizuje taki przypadek. Będzie to dość złożony proces i możesz postąpić w ten sposób:

Stwórz listę z plikami w danym folderze:

Z poziomu pliku .php w templatce html wrzuć taki kawałek kodu:

<?php $out = array();
    foreach (glob('./wp-content/themes/bedekodzic/images/icons/*.png') as $filename) {
        $p = pathinfo($filename);
        $out[] = $p['filename'];
}?>

Czyli tworzysz zmienną   out  będącą tablicą ze wszystkimi plikami png w folderze   /images/ 

Analogicznie postąp z innymi typami plików.

W strukturze DOM dodaj element który będzie niewidoczny i zawiera zmienną z PHP jako dowolny atrybut np.   cache-data  :

<div style="display: none;" id="cache-list" cache-data=<?php echo json_encode($out); ?>></div>

W ten sposób tablica ze wszystkimi plikami jest przypisana do atrybutu w HTML. Trochę to “prymitywne” rozwiązanie ale nie jestem specem od PHP 🙂 Wybacz 🙂 Może masz jakiś lepszy pomysł?

Następnie należy zaimportować te dane do głównego pliku JS. Zrobię to po prostu pobierając element z DOM i potrzebny atrybut:

cacheList () {
    const listElement = document.querySelector('#cache-list');
    const cacheList = listElement.getAttribute('cache-data');
    const cacheListArray = cacheList.replace(/"/g, '').slice(1, -1);

    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
            return serviceWorkerRegistration.pushManager.getSubscription();
        })
        .then(function(subscription) {
            if (!subscription) {}
            navigator.serviceWorker.controller.postMessage(cacheListArray);
        })
    }
}

Komunikacja między SW a JS

Powyżej poza prostym przetworzeniem zmiennej z danymi używam specjalnej metody w Servie Workerze tzn.   postMessage()  . Umożliwia ona komunikację między głównym wątkiem JS a SW:

navigator.serviceWorker.controller.postMessage(cacheListArray);

Pobierz dane z JS do SW

const pathPrefix = '/wp-content/themes/bedekodzic/icons/';
const filesToCache = [
    '/wp-content/themes/bedekodzic/index.php'
];

self.addEventListener('message', function(event){
    event.data.split(',').map(a => {
        filesToCache.push(`${pathPrefix}${a}`);
    });
});

Teraz jeśli weźmiesz poprawkę na asynchroniczność instalacji Service Worker’a i komunikacji z głównym wątkiem JS to będziesz w stanie zdefiniować cache 🙂 Prawda, że proste?

W razie czego zawsze możesz manualnie tworzyć listę cache 🙂

Cachowanie nowych zasobów

Kolejną kwestią którą chcesz rozpatrzeć to aktualizowanie zasobów które już są w cache jeżeli zostały zmodyfikowane. Zazwyczaj odpowiedzią na ten problem jest podejście Network First czyli zanim połączysz się z cache SW to najpierw sprawdzasz sieć. Ten artykuł opisuje to w ciekawy sposób.

WordPress, PWA i SPA

Jeśli chcesz jeszcze przyspieszyć swojego WordPressa to możesz zapakować go w App Shell w zamienić w Single Page Application. Można do tego użyć Vue Router lub Gatsby bazującego na React’cie. Co Ci to da w kontekście PWA?

Wtedy w cache będzie tylko App Shell, grafiki itp. oraz API z WordPress’a czyli dane z serwera. Nie będzie potrzeby przechowywać każdej strony z osobna. No ale to temat na odrębny wpis 🙂

Push Notification

To teraz coś zupełnie nowego. Goodbye cache…

A więc ten mechanizm ma na celu automatyczne wyświetlanie wiadomości użytkownikowi tak jak potrafią to robić aplikacje natywne gdzie dostajemy informacje w powiadomieniach na smartfonie na jakiś temat np. “Super promocja w Naszym sklepie!”.

Ten proces składa się z dwóch kroków:

Push

Jeżeli serwer posiada taką opcję to Back-End automatycznie komunikuje się z Service Workerem wysyłając zawartość powiadomienia. Należy pamiętać, że Service Worker działa niezależnie od głównego wątku aplikacji więc może się komunikować z serwerem w dowolnym momencie.

Notification

Kiedy zasoby SW są uaktualnione jest on w stanie wyświetlić powiadomienie w sposób odpowiedni dla systemu/przeglądarki.

Na samej stronie/aplikacji możesz też mieć dostęp do wszystkich notyfikacji które zostały wysłane i je wyświetlić.

Pozwól na wyświetlanie notyfikacji

Aby poprawnie skonfigurować wysyłanie notyfikacji z serwera jest potrzebne specjalne API. Dodatkowo użytkownik musi się zgodzić na wyświetlanie notyfikacji więc należy przygotować okno które wyświetli się i po zgodzie zostaną zmienione ustawienia w przeglądarce. Dobrze jest ten cały proces opisany TUTAJ.

Ja pójdę na skróty i manualnie zmienię to w ustawieniach Chrome:

Kliknij w kłódkę (lub jej brak) i wejdź w ustawienia strony

Zmień ustawienia notyfikacji na zezwól:

Dobrze to teraz notyfikacje nie będą blokowane.

Push i Service Worker

Teraz czas na uaktualnienie Service Worker’a o taki kod:

// 1. Nowa metoda w SW
self.addEventListener('push', (event) => {
    // 2. Sprawdź wiadomośc z serwera i sparsuj do tekstu
    console.log('Otrzymałem nowe dane z serwera:', event.data.text())
    // 3. Stwórz tytuł i treśc notyfikacji. Uzyj danych z serwera
    const message = {
        data: event.data.text()
    }
    const title = 'Niesamowita sprawa!';

    // 4. Stwórz notyfikację
    const promiseChain = self.registration.showNotification(title, message);
    // 5. Wywołaj notyfikację
    event.waitUntil(promiseChain);
})

Oczywiście jest to bardzo podstawowa konfiguracja. Jeśli potrzebujesz czegoś więcej sprawdź te materiały od Google.

Testowanie Push Notification

Teoretycznie to powinno już działać jednak trzeba sprawdzić. W devToolsach idź do zakładki Aplikacja i powinieneś/aś widzieć swojego Service Worker’a wprowadź jakiś komunikat w odpowiednim polu i naciśnij Push

Jeśli wszystko działa poprawnie to powinna się pojawić notyfikacja z Twojej aplikacji

Renderowanie Notyfikacji

Jeżeli chcesz aby dane z notyfikacji wysłanych przez serwer pojawiły się na twojej stronie możesz się do nich dostać z poziomu głównego wątku JS np. przy okazji rejestracji SW:

registerServiceWorker() {
    if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
        navigator.serviceWorker.register('serviceWorker.js');
        // Pobierz z SW wszystkie notyfikacje
        navigator.serviceWorker.ready.then((registration) => {
            registration.getNotifications().then((notification) => {
                console.log(notification);
            })
        });
    });
}}

No! To teraz już nikt Cię nie zagnie pytaniem o Push Notifications w Service Workerze!

Co dalej?

W tej serii zechcę jeszcze opisać działanie indexedDB i Background Sync więc to jeszcze nie koniec 🙂

Podsumowanie

Dzisiaj pogłębiłeś/aś swoją wiedzę na temat Service Worker’ów:

  1. Wiesz jak możesz tworzyć listy plików do cache na podstawie danych z PHP.
  2. Wiesz jak komunikować główny wątek JS z Service Worker’em i wysyłać między nimi dowolne dane.
  3. Wiesz jak działa Push Notification i potrafisz go użyć.

Fascynujący Świat PWA ma jeszcze wiele tajemnic a to o czym czytasz to zaledwie wstęp 🙂 Znajomość tego tematu zdecydowanie zaprocentuje w przyszłości!

Podziel się z innymi 🙂

Piszę dla was tego bloga bo lubię aplikacje internetowe. Mogę je projektować, kodować a potem o nich pisać czując dreszczyk ekscytacji za każdym razem gdy trafię na coś nowego. Bo uczymy się całe życie. Prawda?

Cześć jestem Bartek.

Na tym blogu wprowadzę Cię w tajniki Front-Endu i programowania webowego.

Warto
Social media & sharing icons powered by UltimatelySocial