Comment faire en sorte qu'un blog Jekyll fonctionne hors ligne à l'aide de Service Workers

Publié: 2017-01-26

Short Bytes : Saviez-vous qu'avec l'avènement des Service Workers, on pourrait commencer à faire fonctionner les sites Web hors ligne ! Ces applications Web sont appelées PWA (Progressive Web Apps). Dans ce tutoriel, je vais vous aider à utiliser un service worker pour faire fonctionner un blog/site web basé sur Jekyll hors ligne et quelques trucs sympas qui vont avec.

(REMARQUE : Les extraits de code de l'article sont tirés du référentiel de code de mon blog. Il peut être consulté si nécessaire. Si vous êtes nouveau sur Jekyll, vous pouvez lire l'article de la série 3 sur les astuces CSS.)

CONTEXTE

Auparavant, il y avait un cache d'application basé sur un fichier yuk YAML, qui était de nature très codée en dur et ne pouvait pas être utilisé pour mettre en cache dynamiquement des actifs et des pages Web. Entrez, travailleurs des services. API Javascript simple basée sur des événements simples pour effectuer une mise en cache dynamique des agents de service pour stocker les actifs, afin qu'ils puissent être utilisés pour servir les pages Web lorsqu'il n'y a pas de réseau.

Les travailleurs du service ont atterri à Chrome Canary en 2014, mais ses spécifications sont toujours en cours de révision/d'ajout et de conception. En 2015 et 2016, l'équipe Chrome de Google a largement fait connaître cette nouvelle technologie dans les navigateurs. Seul Apple n'a pas pris en charge cela (même au moment de la rédaction de cet article) sur leurs appareils (pour des raisons inconnues) [Ils ne participent pas non plus activement aux discussions sur les spécifications concernant les techniciens de service].

Qu'est-ce qu'un travailleur de service ? Fondamentalement, c'est un travailleur du Web sous stéroïdes. L'une des caractéristiques d'un web worker est que toutes les tâches d'un web worker s'exécutent séparément (de manière asynchrone) du fil d'exécution JavaScript principal (boucle d'événement). Cette fonctionnalité permet d'exécuter des tâches gourmandes en CPU ou en mémoire, par exemple des calculs compliqués sans compromettre les performances de votre interface utilisateur de l'application Web.

Service Worker nous permet de mettre en cache (stocker pendant longtemps) des actifs, tels que JavaScript, CSS, HTML, image, fichiers de polices dans le cache Service Worker du navigateur, de sorte que la prochaine fois que l'utilisateur charge cette page, elle est chargée presque instantanément . Et aussi puisque dans cette stratégie de mise en cache, le navigateur recherche d'abord la disponibilité des actifs à partir du cache du service worker, la page Web est servie même lorsque l'on est hors ligne ! Si un actif ne se trouve pas dans le cache, une requête réseau est envoyée pour le récupérer.

Le travailleur de service active également les notifications push qui sont courantes sur de nombreux sites Web de nos jours, notamment Facebook, Whatsapp sur le Web et Twitter. Nous parlerons principalement de la fonctionnalité hors ligne. Ce guide est spécifique à Jekyll, cependant, la plupart du code du service worker peut généralement être appliqué à n'importe quel site Web.

Étant donné que Jekyll sert du contenu statique ( c'est un générateur de site statique, duh ! ), notre code de service worker serait très basique et facile à comprendre.

ALLONS ROULER :

Sur toutes les pages pertinentes, le morceau de script suivant est exécuté. Il fait les choses suivantes :

  1. Vérification de l'existence de l'API de service worker dans le navigateur et enregistrement du service worker.
  2. Lorsque le service worker a été activé, envoyez un joli petit message toast/chip à l'utilisateur indiquant que le site Web est maintenant prêt à être utilisé hors ligne.
 function showOfflineToast() {
  laissez offlineToast = document.querySelector('.offline-ready');
  offlineToast.classList.add('active');
  setTimeout(fonction(){ 
    offlineToast.className = offlineToast.className.replace("active", "").trim();
  }, 5500);
}

// (1)
si (navigator.serviceWorker) {
  navigator.serviceWorker.register('/sw.js').then(function(reg) {
      si (!reg.installing) retour ;
      console.log("[*] ServiceWorker installe...");

      var worker = reg.installing;

      travailleur.addEventListener('changement d'état', fonction() {
          si (worker.state == 'redondant') {
              console.log('[*] Échec de l'installation');
          }
          si (worker.state == 'installé') {
              console.log('[*] Installation réussie !');
          }
          // (2)
          if (worker.state == 'activé' && !navigator.serviceWorker.controller) {
            showOfflineToast();
          }
      });
  });
} 

gdad-s-river.github.io-(iPhone 6)

Vous pouvez ajouter ce morceau de code dans un fichier par exemple serviceWorker.html dans le répertoire includes de votre code Jekyll et l'inclure dans default.html en utilisant le moteur de template liquide de Jekyll

 <!DOCTYPE html>
<html>

  {% incluent head.html %}

  <corps>
    {% inclure header.html %}

    <div class="page-content">
      <div class="wrapper">
        {{ contenu }}
      </div>
    </div>

    {% incluent le pied de page.html %}

    <!-- Contient le code ci-dessus dans une balise de script-->
    {% incluent serviceWorker.html %} 

    <div class="offline-ready">Le site est prêt pour une utilisation hors ligne</div>
  </body>

</html>

Passons maintenant au code de service worker qui fait la magie. Ce code réside dans sw.js à la racine de votre code Jekyll.

 //sw.js
---
mise en page : nulle
---

const staticCacheName = "gdad-s-river-static-v61" ;

console.log("installation de service worker");

const filesToCache = [
  "/",
  {% pour la page dans site.html_pages %}
    '{{ L'URL de la page }}',
  {% endfor %}
  {% pour la publication dans site.posts %}
    '{{ poste.url }}',
  {% endfor %}

  // peut être automatisé plutôt que des entrées manuelles
  "/assets/images/bhavri-github-callbacks.png",
  "/assets/images/bhavri-github-issues.png",
  "/assets/images/jakethecake-svg-line-anime.png",
  "/assets/images/svg-animated-mast-text-shapes-tweet.png",
  "css/main.css",
  "/sur/",
  "/index.html"
] ;

staticCacheName est la version du cache qui doit être mise à jour chaque fois que j'apporte des modifications aux réponses mises en cache (par exemple, je modifie un fichier CSS ou un article de blog). Et, je ne fais que définir les requêtes que je veux intercepter et mettre en cache dans un tableau (utilisé dans l'extrait suivant).

 //sw.js

self.addEventListener("installer", fonction(e){
  self.skipWaiting();
  e.waitUntil(
    caches.open(staticCacheName).then(function(cache){
      return cache.addAll(filesToCache);
    })
  )
});

self.skipWaiting , c'est-à-dire qu'à chaque fois que ce fichier sw.js change, la nouvelle version du service worker ne doit pas être mise en file d'attente, mais rendue active immédiatement (on pourrait demander à l'utilisateur d'actualiser la page en donnant un message comme La page Web a été mise à jour/modifiée, cliquez sur Actualiser pour charger de nouveaux messages ou quoi que ce soit. ), jeter l'ancienne version.

java-et-android-square-ad-1

e.waitUntil citant le site Web de MDN :

« La méthode ExtendableEvent.waitUntil() prolonge la durée de vie de l'événement. Dans les service workers, prolonger la durée de vie d'un événement empêche le navigateur de mettre fin au service worker avant que les opérations asynchrones au sein de l'événement ne soient terminées.

J'ouvre un cache nommé gdad-s-river-static-v61 , qui renvoie une promesse avec mon nom de cache, puis j'appelle cache.addAll (qui utilise l'API de récupération en arrière-plan), qui récupère toutes les requêtes dans le tableau fourni et les met en cache.

Au prochain extrait !

 //sw.js

self.addEventListener("activer", fonction(e){
  e.waitUntil(
    caches.keys().then(fonction(nomscache){
      retour Promise.all(
        cacheNames.filter(fonction(cacheName){
          return cacheName.startsWith("gdad-s-river-static-")
            && cacheName != staticCacheName;
        }).map(fonction(nom du cache){
          return cache.delete(cacheName);
        })
      )ß
    })
  )
});

Lorsque le technicien de service s'active, je m'assure que tout technicien de service qui n'est pas la dernière version est supprimé. Par exemple, si ma dernière version de cache est disons gdad-s-river-static-v61 , et que quelqu'un est toujours sur gdad-s-river-static-v58 , lors de sa prochaine visite, je veux que ce client ne se soucie pas de pompant une version à la fois, mais supprimez carrément cette version pour installer la dernière.

 //sw.js

self.addEventListener("récupérer", fonction(e){
  e.respondWith(
     caches.match(e.request).then(function(response) {
       réponse de retour || récupérer(e.request);
     })
   )
});

Dans l'événement de récupération, je dis simplement au technicien de service comment répondre à une demande particulière faite (puisque nous détournons la réponse donnant le pouvoir, les techniciens de service ne travaillent que sur des sites Web sécurisés d'origine https). Je lui dis de faire correspondre la demande à celles mises en cache dans le navigateur, et s'il ne trouve pas cette réponse particulière à la demande, récupérez-la via le réseau, sinon servez-la à partir du cache.

Tada ! Un travailleur de service a mis hors ligne le blog propulsé par Jekyll !

SURPRENDRE! LA CHOSE COOL :

Bummer : Cela ne fonctionnerait pas sur les appareils iOS.

Si vous ajoutez un fichier manifest.json d'application Web à la racine du projet comme ceci :

 {
  "name": "gdad-s-river",
  "short_name": "gdad-s-rivière",
  "theme_color": "#2196f3",
  "background_color": "#2196f3",
  "affichage": "autonome",
  "Portée": "/",
  "start_url": "/",
  "Icônes": [
    {
     "src": "assets/images/favicon_images/android-icon-36x36.png",
     "tailles": "36x36",
     "type": "image\/png",
     "densité": "0,75"
    },
    {
     "src": "assets/images/favicon_images/android-icon-48x48.png",
     "tailles": "48x48",
     "type": "image\/png",
     "densité": "1.0"
    },
    {
     "src": "assets/images/favicon_images/android-icon-72x72.png",
     "tailles": "72x72",
     "type": "image\/png",
     "densité": "1.5"
    },
    {
     "src": "assets/images/favicon_images/android-icon-96x96.png",
     "tailles": "96x96",
     "type": "image\/png",
     "densité": "2.0"
    },
    {
     "src": "assets/images/favicon_images/android-icon-144x144.png",
     "tailles": "144x144",
     "type": "image\/png",
     "densité": "3.0"
    },
    {
     "src": "assets/images/favicon_images/android-icon-192x192.png",
     "tailles": "192x192",
     "type": "image\/png",
     "densité": "4.0"
    }
  ]
}

et ajoutez-le dans le fichier head.html à l'intérieur de la balise head,

 <tête>
 <!-- quelques trucs -->
	<link rel="manifest" href="/manifest.json">
 <!-- quelques trucs -->
</head>

Ensuite, lors de la deuxième visite de votre site Web (dans les 5 minutes), l'utilisateur sera invité à ajouter votre site Web à votre écran d'accueil (pour résider avec une icône, tout comme les autres applications natives), avec laquelle vous serez engagé tout comme Une application.

Capture d'écran_20170123-232947

RESSOURCES

  • Les détails des fonctionnalités hors ligne et des stratégies de mise en cache du travailleur de service peuvent être trouvés dans le livre de recettes hors ligne de ce génial Jake Archibald.
  • Un cours Udacity gratuit et très détaillé sur tout ce que vous devez savoir sur les service workers et IndexDB.

Avez-vous trouvé cet article sur Jekyll Blog intéressant et utile ? N'oubliez pas de partager vos points de vue et vos commentaires.

A lire aussi : Ecosia — Le moteur de recherche qui plante des arbres