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

Rozumiem

Progressive Web App. Tworzę manifest.json i Service Worker.

20/2/2019
Bartek Cis

W tym wpisie dowiesz się jak stworzyć Progressive Web App na WordPress’ie. Na początek potrzebujesz pliku manifest.json i Service Workera.

Czego się dzisiaj dowiesz?

Będzie o pierwszych krokach niezbędnych do budowy PWA w oparciu o WordPress czyli opiszę czym jest manifest.json oraz jak zainstalować i uruchomić Service Worker’a. Tak naprawdę te same zasady obowiązują w przypadku pracy z każdym innym rodzajem aplikacji webowej!

Jeśli chcesz zbudować PWA na WordPressie to koniecznie sprawdź inne moje artykuły z tej serii.

Plan działania:

  1. Manifest.json
  2. Service Worker

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.

Zanim zaczniesz

Nie będę tu tłumaczył podstawowych mechanizmów WordPress’a. Musisz wiedzieć jak z nim pracować.

Zakładam, że masz czystą instalację WordPress’a działającą na lokalnym środowisku. Ma ona działać przez protokół HTTPS. W poprzednim artykule z tej serii opisałem jak możesz to zrobić.

Kolejnym krokiem jest wgranie motywu na którym będziesz pracować. Po prostu skopiuj kod do folderu wp-content/themes/twoj_motyw

Jeśli nie masz jeszcze żadnego narzędzia do automatyzacji to użyj mojej konfiguracji Webpacka 4 🙂

3…2…1… Start!

Zanim zacznę to print audytu początkowego:

Manifest.json

Jest to plik który opisuje jak aplikacja powinna się zachować po zainstalowaniu Service Worker’a na danym urządzeniu. Zawiera dość podstawowe właściwości o których powiem za moment.

Ma on duże znaczenie zwłaszcza na smartfonach bo dzięki niemu apka może mieć własną ikonę i imitować aplikację natywną.

Ten plik umieszcza się zazwyczaj w głównym katalogu projektu (aczkolwiek może być gdziekolwiek indziej tak długo jak go odpowiednio podepniesz). Ma format JSON i można go stworzyć manualnie aczkolwiek polecam ten generator. Na potrzeby developmentu mój plik wygląda tak:

{
    "name": "Będękodzić - Web Development i Programowanie",
    "short_name": "Bedekodzić",
    "theme_color": "#ec1b62",
    "background_color": "#2c3030",
    "display": "standalone",
    "Scope": "/wp-content/themes/bedekodzic/",
    "start_url": "https://wppwa.local/wp-content/themes/bedekodzic",
    "icons": [
        {
            "src": "images/icons/icon-72x72.png",
            "sizes": "72x72",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-96x96.png",
            "sizes": "96x96",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-128x128.png",
            "sizes": "128x128",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-144x144.png",
            "sizes": "144x144",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-152x152.png",
            "sizes": "152x152",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-192x192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-384x384.png",
            "sizes": "384x384",
            "type": "image/png"
        },
        {
            "src": "images/icons/icon-512x512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "splash_pages": null
}

Najważniejsze właściwości

Czyli to co trzeba mieć na uwadze.

Scope

"Scope": "/wp-content/themes/bedekodzic/",

Zakres zasobów które są brane pod uwagę przy PWA. W tym wypadku wszystko co należy do folderu z tym motywem. W zasadzie to ma charakter informacyjny bo potem zakres jest kontrolowany z poziomu Service Workera.

Start URL

"start_url": "https://wppwa.local/wp-content/themes/bedekodzic/",

Ekran który ma się odpalić na telefonie po kliknięciu w ikonę z aplikacją. W tym wypadku strona główna. Jeśli pracujesz z WordPressem to powinien to być folder do Twojego motywu z zasobami. Jeśli wstawisz tu zły link to nie wykryjesz pliku manifest.json

Background Color

"background_color": "#2c3030",

Czyli tło ekranu używanego przy uruchamianiu aplikacji na telefonie.

Icons

"icons": []

Cały zestaw ikon dobierany w zależności od rozdzielczości.

Splash Page

"splash_pages": null

Czyli jakiś niestandardowy ekran ładowania aplikacji. Jeśli jest ustawiony na null to będzie kombinacją tła i ikony.

Display

"display": "standalone",

Tryb w jakim apka wyświetli się na telefonie tzn. standalone znaczy pominięcie paska adresu w przeglądarce i wszystko wygląda podobnie do apki natywnej.

Generalnie odsyłam do linku w tytule paragrafu. Jest to tam dość dobrze wytłumaczone.

Podpięcie Manifestu

Trzeba podpiąć w HTML do tagu head​podobnie jak np. style w CSS. Teoretycznie powinno się to zrobić po prostu tak:

<link rel="manifest" href="/manifest.json">

Ale WordPress może nie być tak sprytny żeby pobrać ten zasób więc użyj bezpośredniego linku np:

<link rel="manifest" href="https://twojastrona/manifest.json">

A jeszcze lepiej jak zrobisz to z poziomu pliku functions.php i dodasz coś takiego:

// Creates the link tag
function inc_manifest_link() {  
    echo '<link rel="manifest" href="https://twojastrona/manifest.json">';
}

add_action( 'wp_head', 'inc_manifest_link' );

Test w Lighthouse

Jeśli wszystko wykonałeś/aś poprawnie to uruchom audyt na PWA i sprawdź czy przeglądarka widzi manifest. Ranking PWA powinien już się nieco poprawić 🙂

Service Worker

Kolejny krok. Jeśli chcesz dowiedzieć się więcej  na temat Service Worker’a to sprawdź poprzedni wpis z tej serii.

W tym artykule skupię się na podstawowych funkcjach Service Worker’a aby go zoptymalizować pod koniec tej serii.

Testowanie na lokalnym środowisku.

Tutaj kolejny haczyk i coś co może Ci się przydać 🙂

Pomimo, że Chrom uruchamia Twojego WordPress’a przez HTTPS to ciągle nie traktuje go jako zaufaną witrynę bo uważa, że Twój certyfikat jest Self-Assigned. Nie pozwoli Ci na rejestrację Service Worker’a i będzie wywalał błędy. Należy uruchomić go w trybie trust Self-Assigned Certificates i w ten sposób go testować.

Chrome – trust Self-Assigned Certificates

Jak jesteś na Mac’u to wpisz w terminalu tą komendę:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/foo --ignore-certificate-errors --unsafely-treat-insecure-origin-as-secure=https://TWOJA-WITRYNA

Jeśli na Windowsie to:

C:\Program Files (x86)\Google\Chrome\Application\chrome.exe --ignore-certificate-errors --unsafely-treat-insecure-origin-as-secure=https://TWOJA-WITRYNA

Jeżeli na Linuksie to już powinieneś wiedzieć co masz zrobić 🙂

Teraz uruchomi Ci się Chrome w nowym oknie. Wpisz swój adres i od teraz pracuj właśnie tutaj.

Rejestracja Service Worker’a

Dobrze czyli manifest.json jest aktywny a przeglądarka ufa środowisku na którym pracujesz. Czas na Service Worker’a.

W swoim głównym pliku JS należy umieścić taki skrypt:

function registerServiceWorker() {
    // 1. Sprawdź czy Service Worker jest obsługiwany w przeglądarce
    if ('serviceWorker' in navigator) {
        // 2. Kiedy strona jest załadowana
        window.addEventListener('load', function() {
        // 3. Zarejestruj SW zdefiniowany w pliku znajdującym się w katalogu
        navigator.serviceWorker.register('serviceWorker.js')
        // 4. Jeśli rejestracja się powiodła to wyświetl komunikat sukcesu
        .then(function(registration) {
            console.log('ServiceWorker registration successful with scope: ', registration.scope);
            }, function(err) {
        // 5. Jeśli rejestracja się nie powiodła to wyświetl komunikat błędu
            console.log('ServiceWorker registration failed: ', err);
            });
        });
    }
}

registerServiceWorker();

Komentarze powyżej mniej więcej tłumaczą najważniejsze kroki przy rejestracji ale jeszcze dodatkowe info.

Jak pierwszy raz rejestrujesz SW to te komunikaty w console.log są bardzo przydatne. Jeśli SW się zarejestruje to widzisz zmienną scope. Ona określa które pliki w aplikacji będą mogły pracować z SW np. jeśli widzisz / to wszystkie pliki w aplikacji będą dostępne. Będziesz mógł/a to potem zmienić.

ES6

Wpleć to w klasę JS np. tak:

class yourClass {
    constructor() {}

    initialize() {
        this.registerServiceWorker();
    }

    registerServiceWorker() {
        if ('serviceWorker' in navigator) {
            window.addEventListener('load', function() {
                navigator.serviceWorker.register('serviceWorker.js')
            });
        }
    }
}

const yourPage = new yourClass();

yourPage.initialize();

Gdy Twój skrypt wykonuje wiele różnych metod/funkcji przy starcie to umieść rejestrację SW na samym początku.

Prawidłowa struktura plików

Od teraz wszystkie operacje będą się odbywać w obrębie pliku serviceWorker.js który został zdefiniowany przy rejestracji.

Jeśli spojrzysz ponownie na rejestrację to zauważysz, że serviceWorker.js nie posiada żadnej ścieżki do katalogu jest tak dlatego, że ten plik musi znajdować się w głównym katalogu aplikacji!

Jeżeli motyw z WordPressa znajduje się w katalogu /wp-content/themes/bedekodzic/ to nie możesz tam umieścić SW. Bezpośredni link do SW musi wyglądać tak https://twojadomena.com/serviceWorker.js  czyli mieć ścieżkę dostępu /serviceWorker.js. To samo dotyczy wszystkich innych aplikacji!

Taka struktura plików umożliwia uruchomienie metody fetch w SW i komunikację między aplikacją a SW. Jak o tym zapomnisz to możesz się trochę napocić aby uruchomić PWA…

Instalacja Service Worker’a

Wracam do kodu. Aby zainstalować SW użyj tego kodu:

self.addEventListener('install', event => {
    console.log('Worker installed successfully', event);
});

Jeśli do tej pory wszystko zrobiłeś/aś dobrze to w konsoli powinna pojawić się informacja o pomyślnej instalacji SW.

Service Worker Cache

Na tym etapie definiujemy też jakie zasoby z aplikacji mają trafić do cache Service Worker’a. Robi się to tak:

self.addEventListener('install', event => {
    // 1. Specjalna metoda SW która czeka na poprawną instalację by przejsc do kolejnych kroków
    event.waitUntil(
        // 2. Tworzysz cache o nazwie 'v1' w którym będą przechowywane konkretne zasoby
        caches.open('v1').then(cache => {
            return cache.addAll([
                // 3. Tworzysz listę plików które mają byc tym cache podaj pośrednie linki tak jak w przykładzie
                '/wp-content/themes/bedekodzic/index.php',
                '/wp-content/themes/bedekodzic/main.bundle.js'
            ]);
        }, error => {
            // 4. W razie błędów
            console.log(`Installation failed with error: ${error}`);
        })
    );
});

Teraz masz już stworzony mechanizm cache dla tego Service Worker’a. Możesz tu zdefiniować co ma się w nim znaleźć 🙂

Aktywacja Service Worker’a

Kolejnym krokiem jest aktywacja. Robi się to tak:

self.addEventListener('activate', event => {
    console.log('Worker activated successfully', event);
});

I w zasadzie SW jest już aktywny i gotowy do pracy jednak krok activate ma jedną bardzo przydatną moc… Cache SW ma ograniczony rozmiar i dzieli miejsce przypisane przez przeglądarkę wraz z klasycznymi kontenerami Web Storage więc trzeba mieć rękę na pulsie aby nie zabrakło miejsca.

Przy procesie instalacji definiujesz nazwę storage np. ‘v1’. Z czasem możesz tworzyć nowe kontenery a dane ze starych mogą dalej siedzieć w cache i je zapychać. Dobrze czyścić stare zasoby w ten sposób:

self.addEventListener('activate', event => {
    // 1. Lista cache które mają zostac w pamieci
    let cacheKeepList = ['v1'];
    event.waitUntil(
        // 2. Lista dostępnych cache
        caches.keys().then( keyList => {
            // 3. Sprawdź kolejno kazdo cache w SW
            return Promise.all(keyList.map(function(key) {
            // 4. Jeśli cache nie jest aktualne to je usuń
            if (cacheKeepList.indexOf(key) === -1) {
                return caches.delete(key);
                }
            }));
        })
    );
});

To teraz jest czysto i pachnąco 🙂

Service Worker Fetch

No dobrze więc SW jest aktywny i ma w cache określone zasoby. Teraz należy jeszcze poinformować aplikację, że ma pobierać zasoby w pierwszej kolejności z SW a nie z serwera:

self.addEventListener('fetch', event => {
    // 1. Jeśli nie pobierasz zasobów to nie uruchamiasz mechanizmu
    if (event.request.method != 'GET') return;
    // 2. Analiza wyników
    event.respondWith(async function() {
        // 3. Sprawdzasz zawarotśc cache i porownojesz z wynikami GETow
        const cache = await caches.open('v1');
        const cachedResponse = await cache.match(event.request);
            if (cachedResponse) {
                // 4. Jesli cache sie zgadza z pobieranymi zasobami to uzyj cache SW
                // i uzupelnij cache o nowe zasoby ktore sie pojawiaja
                event.waitUntil(cache.add(event.request));
                return cachedResponse;
            }
        // 5. Jesli zadnych potrzebych zasobow nie ma w zdefiniowanym cache to pobieraj z sieci
        return fetch(event.request);
    }());
});

A więc teraz nasz SW posiada wszystkie niezbędne funkcje!

Offline First

Powyższa konfiguracja najpierw sprawdza czy dane są w cache SW a dopiero potem pobiera z sieci. Jest to podejście offline first.

serviceWorker.js

Zbieram cały plik do kupy:

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

self.addEventListener('activate', event => {
    let cacheKeepList = ['v1'];
    
    event.waitUntil(
        caches.keys().then( keyList => {
            return Promise.all(keyList.map(function(key) {
                if (cacheKeepList.indexOf(key) === -1) {
                    return caches.delete(key);
                }
            }));
        })
    );
});

self.addEventListener('fetch', event => {
    if (event.request.method != 'GET') return;
    
    event.respondWith(async function() {
        const cache = await caches.open('v1');
        const cachedResponse = await cache.match(event.request);
        
        if (cachedResponse) {
            event.waitUntil(cache.add(event.request));
            return cachedResponse;
        }
        
        return fetch(event.request);
    }());
});

Debugowanie Service Worker’a

Używa się do tego devToolsów w Chromie w zakładce Aplikacja:

Jeżeli Twój SW działa prawidłowo to zobaczysz jak apka ładuje zasoby w zakładce Network:

Test w Lighthouse

No dobra to teraz czas na ostateczne rozstrzygnięcie powodzenia operacji:

Ten wynik mówi sam za siebie…

Co dalej?

To tyle jeśli chodzi o ten wpis ale ta seria ma jeszcze wiele do zaoferowania… W kolejnych odcinkach wprowadzę kilka poprawek do mojego WordPressa. Dodam do Service Worker’a push, bacgroundSync i udoskonale cache 🙂 Będzie grubo! Będę Kodzić 🙂

Podsumowanie

W dzisiejszym wpisie pogłębiłeś/aś swoją wiedzę o Progressive Web App.

  1. Wiesz dobrze czym są Service Workery i plik manifest.json.
  2. Potrafisz zmienić WordPressa i każdą inną Appkę w PWA.

Jesteś gotowy/a do dalszych wyzwań i Twoje aplikacje robią się coraz lepsze 🙂

Podziel się z innymi 🙂

Cześć jestem Bartek.

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

Warto
Social media & sharing icons powered by UltimatelySocial