Cómo hacer que un blog de Jekyll funcione sin conexión usando Service Workers

Publicado: 2017-01-26

Bytes cortos: ¿Sabía que con la llegada de Service Workers, uno podría comenzar a hacer que los sitios web funcionen sin conexión? Estas aplicaciones web se denominan PWA (aplicaciones web progresivas). En este tutorial, lo ayudaré a usar un trabajador de servicio para hacer que un blog/sitio web basado en Jekyll funcione sin conexión y algunas cosas geniales que vienen con él.

(NOTA : los fragmentos de código en el artículo se tomaron del repositorio de código de mi blog. Se puede consultar si es necesario. Si es nuevo en Jekyll, puede leer el artículo de la serie 3 sobre trucos CSS).

ANTECEDENTES

Anteriormente, solía haber un caché de aplicación basado en un archivo YAML de yuk, que estaba muy codificado por naturaleza y no podía usarse para almacenar en caché activos y páginas web de forma dinámica. Entrar, trabajadores de servicio. API de Javascript simple basada en eventos simples para realizar el almacenamiento en caché de trabajadores de servicios dinámicos para almacenar activos, de modo que puedan usarse para servir las páginas web cuando no hay red.

Los trabajadores del servicio aterrizaron en Chrome Canary en 2014, pero sus especificaciones aún se están revisando, agregando y diseñando. En 2015 y 2016, el equipo Chrome de Google difundió la noticia de esta nueva tecnología en los navegadores. Solo Apple no ha admitido esto (incluso al momento de escribir este artículo) en sus dispositivos (por razones desconocidas) [Tampoco participan activamente en ninguna discusión sobre especificaciones sobre trabajadores de servicios].

¿Qué es un trabajador de servicios? Básicamente, es un trabajador web con esteroides. Una característica de un trabajador web es que todas las tareas de un trabajador web se ejecutan por separado (asincrónicamente) desde el subproceso principal de ejecución de JavaScript (bucle de eventos). Esta característica ayuda a ejecutar tareas intensivas de CPU o memoria, por ejemplo, cálculos complicados sin comprometer el rendimiento de su interfaz de usuario de la aplicación web.

Service Worker nos permite almacenar en caché (almacenar durante mucho tiempo) activos, como JavaScript, CSS, HTML, imágenes, archivos de fuentes en el caché de Service Worker del navegador, por lo que la próxima vez que el usuario carga esa página, se carga casi instantáneamente. . Y también dado que en esta estrategia de almacenamiento en caché, el navegador busca la disponibilidad de los activos primero desde el caché del trabajador del servicio, ¡la página web se sirve incluso cuando uno está fuera de línea! Si algún activo no está en el caché, se envía una solicitud de red para recuperarlo.

El trabajador del servicio también habilita las notificaciones automáticas que son comunes en muchos sitios web en estos días, incluidos Facebook, Whatsapp en la web y Twitter. Hablaremos principalmente de la función sin conexión. Este tutorial es específico para Jekyll, sin embargo, la mayor parte del código del trabajador del servicio se puede aplicar generalmente a cualquier sitio web.

Dado que Jekyll sirve contenidos estáticos ( ¡es un generador de sitios estáticos, claro! ), Nuestro código de trabajador de servicio sería muy básico y fácil de entender.

VAMOS A RODAR:

En todas las páginas relevantes, se ejecuta el siguiente script. Está haciendo las siguientes cosas:

  1. Comprobación de la existencia de la API del trabajador del servicio en el navegador y registro del trabajador del servicio.
  2. Cuando el trabajador del servicio se haya activado, envíe un pequeño mensaje de brindis/chip al usuario de que el sitio web está listo para usarse sin conexión ahora.
 función mostrarOfflineToast() {
  let offlineToast = document.querySelector('.offline-ready');
  offlineToast.classList.add('activo');
  establecerTiempo de espera (función () { 
    offlineToast.className = offlineToast.className.replace("activo", "").trim();
  }, 5500);
}

// (1)
si (navegador.serviceWorker) {
  navigator.serviceWorker.register('/sw.js').then(function(reg) {
      if (!reg.instalación) regresa;
      console.log("[*] ServiceWorker está instalando...");

      var trabajador = reg.instalando;

      trabajador.addEventListener('cambio de estado', función() {
          if (trabajador.estado == 'redundante') {
              console.log('[*] Instalación fallida');
          }
          if (trabajador.estado == 'instalado') {
              console.log('[*] ¡Instalación exitosa!');
          }
          // (2)
          if (trabajador.estado == 'activado' && !navigator.serviceWorker.controller) {
            showOfflineToast();
          }
      });
  });
} 

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

Puede agregar este fragmento de código en un archivo, digamos serviceWorker.html dentro del directorio de inclusión de su código Jekyll e incluirlo en default.html usando el motor de plantillas líquidas de Jekyll.

 <!DOCTYPEhtml>
<html>

  {% incluye cabeza.html%}

  <cuerpo>
    {% incluir encabezado.html%}

    <div class="contenido-de-pagina">
      <div class="envoltorio">
        {{ contenido }}
      </div>
    </div>

    {% incluir pie de página.html%}

    <!-- Contiene el código anterior en una etiqueta de secuencia de comandos-->
    {% incluyen serviceWorker.html%} 

    <div class="offline-ready">El sitio está listo para su uso sin conexión</div>
  </cuerpo>

</html>

Ahora al código de trabajador de servicio real que hace la magia. Este código reside en sw.js en la raíz de su código Jekyll.

 //sw.js
---
diseño: nulo
---

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

console.log("trabajador del servicio de instalación");

const archivos a caché = [
  "/",
  {% para la página en site.html_pages %}
    '{{ URL de la página }}',
  {% endfor%}
  {% para publicación en sitio.publicaciones %}
    '{{ publicación.url }}',
  {% endfor%}

  // se puede automatizar en lugar de entradas manuales
  "/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/principal.css",
  "/sobre/",
  "/index.html"
];

staticCacheName es la versión de caché que se actualizará cada vez que realice algunos cambios en las respuestas almacenadas en caché (por ejemplo, realizo un cambio en, por ejemplo, un archivo CSS o una publicación de blog). Y solo estoy definiendo qué solicitudes quiero interceptar y almacenar en caché en una matriz (que se usa en el siguiente fragmento).

 //sw.js

self.addEventListener("instalar", función(e){
  self.skipWaiting();
  e.esperar hasta(
    caches.open(staticCacheName).then(function(cache){
      return cache.addAll(filesToCache);
    })
  )
});

self.skipWaiting , es decir, que cada vez que cambia este archivo sw.js , la versión más nueva del trabajador del servicio no debe ponerse en cola, sino activarse de inmediato (se podría solicitar al usuario que actualice la página dando un mensaje como La página web se ha actualizado/modificado, haga clic en Actualizar para cargar nuevas publicaciones o lo que sea. ), descartando la versión anterior.

java-y-android-square-ad-1

e.waitUntil citando del sitio web de MDN:

“El método ExtendableEvent.waitUntil() extiende la duración del evento. En los trabajadores del servicio, extender la vida de un evento evita que el navegador termine el trabajador del servicio antes de que se completen las operaciones asincrónicas dentro del evento”.

Abro un caché llamado gdad-s-river-static-v61 , que devuelve una promesa con el nombre de mi caché, y luego llamo a cache.addAll (que usa la API de obtención en segundo plano), que obtiene todas las solicitudes en el matriz proporcionada y los almacena en caché.

¡Pasamos al siguiente fragmento!

 //sw.js

self.addEventListener("activar", función(e){
  e.esperar hasta(
    caches.keys().then(function(cacheNames){
      volver Promise.all(
        cacheNames.filter(función(cacheName){
          return cacheName.startsWith("gdad-s-river-static-")
            && cacheName != staticCacheName;
        }).map(función(nombreCaché){
          return cache.delete(cacheName);
        })
      )ß
    })
  )
});

Cuando se activa el trabajador del servicio, me aseguro de que se elimine cualquier trabajador del servicio que no sea la última versión. Por ejemplo, si mi última versión de caché es gdad-s-river-static-v61 y alguien todavía está en gdad-s-river-static-v58 , en su próxima visita, quiero que ese cliente no se preocupe por bombeando una versión a la vez, pero elimine directamente esa versión para instalar la última.

 //sw.js

self.addEventListener("buscar", función(e){
  e.respondWith(
     caches.match(e.request).then(función(respuesta) {
       devolver respuesta || buscar (e.solicitud);
     })
   )
});

En el evento de obtención, simplemente le digo al trabajador del servicio cómo responder a una solicitud particular realizada (dado que estamos secuestrando el poder de respuesta, los trabajadores del servicio solo trabajan en sitios web seguros, también conocidos como https). Le digo que haga coincidir la solicitud con las almacenadas en caché en el navegador, y si no encuentra esa respuesta particular a la solicitud, la busque a través de la red, de lo contrario, la servirá desde el caché.

¡Tada! ¡El trabajador del servicio creó un blog potenciado por Jekyll fuera de línea!

¡SORPRESA! LO GENIAL:

Bummer: Esto no funcionaría en dispositivos iOS.

Si agrega un archivo manifest.json de aplicación web en la raíz del proyecto de esta manera:

 {
  "nombre": "río-gdad",
  "short_name": "gdad-s-river",
  "tema_color": "#2196f3",
  "background_color": "#2196f3",
  "pantalla": "independiente",
  "Alcance": "/",
  "start_url": "/",
  "iconos": [
    {
     "src": "activos/imágenes/favicon_images/icono-de-android-36x36.png",
     "tamaños": "36x36",
     "tipo": "imagen\/png",
     "densidad": "0,75"
    },
    {
     "src": "activos/imágenes/favicon_images/icono-de-android-48x48.png",
     "tamaños": "48x48",
     "tipo": "imagen\/png",
     "densidad": "1.0"
    },
    {
     "src": "activos/imágenes/favicon_images/icono-de-android-72x72.png",
     "tamaños": "72x72",
     "tipo": "imagen\/png",
     "densidad": "1.5"
    },
    {
     "src": "activos/imágenes/favicon_images/icono-de-android-96x96.png",
     "tamaños": "96x96",
     "tipo": "imagen\/png",
     "densidad": "2.0"
    },
    {
     "src": "activos/imágenes/favicon_images/icono-de-android-144x144.png",
     "tamaños": "144x144",
     "tipo": "imagen\/png",
     "densidad": "3.0"
    },
    {
     "src": "activos/imágenes/favicon_images/icono-de-android-192x192.png",
     "tamaños": "192x192",
     "tipo": "imagen\/png",
     "densidad": "4.0"
    }
  ]
}

y agréguelo en el archivo head.html dentro de la etiqueta de la cabeza,

 <cabeza>
 <!-- algunas cosas -->
	<enlace rel="manifiesto" href="/manifiesto.json">
 <!-- algunas cosas -->
</cabeza>

Luego, en la segunda visita a su sitio web (dentro de los 5 minutos), se le pedirá al usuario que agregue su sitio web a su pantalla de inicio (para que resida con un ícono, al igual que otras aplicaciones nativas), con lo que participará como Una aplicación.

Captura de pantalla_20170123-232947

RECURSOS

  • Los detalles de las funciones fuera de línea del trabajador del servicio y las estrategias de almacenamiento en caché se pueden encontrar en este increíble libro de cocina fuera de línea de Jake Archibald.
  • Un curso gratuito de Udacity muy detallado sobre todo lo que necesita saber sobre los trabajadores de servicios e IndexDB.

¿Encontró este artículo en Jekyll Blog interesante y útil? No olvides compartir tus opiniones y comentarios.

Lea también : Ecosia: el motor de búsqueda que planta árboles