Saltar al contenido principal

Lo que el equipo de Bulletin aprendió sobre los trabajadores de servicio mientras desarrollaba una PWA.

Esta es la primera de una serie de publicaciones de blog sobre las lecciones que aprendió el equipo de Google Bulletin al crear una PWA externa. En estas publicaciones compartiremos algunos de los desafíos que enfrentamos, los enfoques que tomamos para superarlos y consejos generales para evitar trampas. Esta no es de ninguna manera una descripción general completa de las PWA. El objetivo es compartir los aprendizajes de la experiencia de nuestro equipo.

Para esta primera publicación, primero cubriremos un poco de información general y luego profundizaremos en todo lo que aprendimos sobre los trabajadores de servicios.

Bulletin se cerró en 2019 debido a la falta de ajuste de producto / mercado. ¡Aún aprendimos mucho sobre las PWA a lo largo del camino!

igraal_es-es

Antecedentes

Bulletin estuvo en desarrollo activo desde mediados de 2017 hasta mediados de 2019.

Por qué elegimos construir una PWA

Antes de profundizar en el proceso de desarrollo, examinemos por qué crear una PWA fue una opción atractiva para este proyecto:

  • Capacidad para iterar rápidamente. Especialmente valioso ya que Bulletin se probaría en múltiples mercados.
  • Base de código único. Nuestros usuarios se dividieron aproximadamente en partes iguales entre Android e iOS. Una PWA significaba que podíamos crear una única aplicación web que funcionara en ambas plataformas. Esto aumentó la velocidad y el impacto del equipo.
  • Actualizado rápidamente e independientemente del comportamiento del usuario. Las PWA pueden actualizarse automáticamente, lo que reduce la cantidad de clientes desactualizados en la naturaleza. Pudimos impulsar cambios importantes en el backend con un tiempo de migración muy corto para los clientes.
  • Se integra fácilmente con aplicaciones propias y de terceros. Tales integraciones eran un requisito para la aplicación. Con una PWA, a menudo significaba simplemente abrir una URL.
  • Se eliminó la fricción de instalar una aplicación.

Nuestro marco

Para Bulletin, usamos Polímero, pero cualquier marco moderno y bien soportado funcionará.

Lo que aprendimos sobre los trabajadores de servicios

No puedes tener una PWA sin una trabajador del servicio. Los trabajadores de servicios le brindan mucho poder, como estrategias avanzadas de almacenamiento en caché, capacidades fuera de línea, sincronización en segundo plano, etc. Si bien los trabajadores de servicios agregan algo de complejidad, descubrimos que sus beneficios superan la complejidad adicional.

Generalo si puedes

Evite escribir un script de trabajador de servicio a mano. Escribir trabajadores de servicios a mano requiere administrar manualmente los recursos almacenados en caché y reescribir la lógica que es común a la mayoría de las bibliotecas de trabajadores de servicios, como Neceser de costura.

Dicho esto, debido a nuestra pila de tecnología interna, no pudimos usar una biblioteca para generar y administrar nuestro trabajador de servicio. Nuestros aprendizajes a continuación reflejarán en ocasiones eso. Vaya a Problemas para los trabajadores de servicios no generados para leer más.

No todas las bibliotecas son compatibles con los trabajadores de servicios

Algunas bibliotecas de JS hacen suposiciones que no funcionan como se esperaba cuando las ejecuta un trabajador del servicio. Por ejemplo, asumiendo window o document están disponibles, o usando una API no disponible para los trabajadores del servicio (XMLHttpRequest, almacenamiento local, etc.). Asegúrese de que las bibliotecas críticas que necesite para su aplicación sean compatibles con los trabajadores de servicios. Para esta PWA en particular, queríamos usar
gapi.js para la autenticación, pero no pudieron hacerlo porque no admitía a los trabajadores del servicio. Los autores de bibliotecas también deben reducir o eliminar suposiciones innecesarias sobre el contexto de JavaScript siempre que sea posible para respaldar los casos de uso de trabajadores de servicios, como evitar API incompatibles con trabajadores de servicios y evitar el estado global.

Evite acceder a IndexedDB durante la inicialización

No leas IndexedDB al inicializar su script de trabajador de servicio, o puede entrar en esta situación no deseada:

  1. El usuario tiene una aplicación web con IndexedDB (IDB) versión N
  2. Se incluye una nueva aplicación web con la versión N + 1 del BID
  3. El usuario visita PWA, que activa la descarga de un nuevo trabajador de servicio
  4. El nuevo trabajador de servicios lee del BID antes de registrarse install manejador de eventos, activando un ciclo de actualización del BID para pasar de N a N + 1
  5. Dado que el usuario tiene un cliente antiguo con la versión N, el proceso de actualización del trabajador del servicio se bloquea ya que las conexiones activas todavía están abiertas a la versión anterior de la base de datos
  6. El trabajador de servicio se cuelga y nunca instala

En nuestro caso, el caché se invalida en la instalación del trabajador del servicio, por lo que si el trabajador del servicio nunca se instaló, los usuarios nunca recibieron la aplicación actualizada.

Hazlo resistente

Aunque los scripts de los trabajadores de servicios se ejecutan en segundo plano, también se pueden finalizar en cualquier momento, incluso cuando se encuentran en medio de operaciones de E / S (red, IDB, etc.). Cualquier proceso de larga duración debería poder reanudarse en cualquier momento.

En el caso de un proceso de sincronización que cargó archivos grandes al servidor y se guardó en el BID, nuestra solución para las cargas parciales interrumpidas fue aprovechar el sistema reanudable de nuestra biblioteca de carga interna, guardando la URL de carga reanudable en el BID antes de cargar y usar esa URL para reanudar una carga si no se completó la primera vez. Además, antes de cualquier operación de E / S de larga duración, el estado se guardaba en IDB para indicar en qué parte del proceso estábamos para cada registro.

No dependas del estado global

Debido a que los trabajadores del servicio existen en un contexto diferente, muchos símbolos que podría esperar que existan no están presentes. Gran parte de nuestro código se ejecutó tanto en window contexto, así como un contexto de trabajador de servicio (como registro, banderas, sincronización, etc.). El código debe estar a la defensiva con respecto a los servicios que utiliza, como el almacenamiento local o las cookies. Puedes usar
globalThis

para referirse al objeto global de una manera que funcione en todos los contextos. También use los datos almacenados en variables globales con moderación, ya que no hay garantía de cuándo se terminará el script y se desalojará el estado.

Desarrollo local

Un componente importante de los trabajadores de servicios es el almacenamiento en caché de los recursos localmente. Sin embargo, durante el desarrollo este es el opuesto de lo que desea, especialmente cuando las actualizaciones se realizan con pereza. Aún desea instalar el trabajador del servidor para poder depurar problemas con él o trabajar con otras API, como sincronización en segundo plano o notificaciones. En Chrome, puede lograr esto a través de Chrome DevTools habilitando el Bypass para red casilla de verificaciónSolicitud panel> Trabajadores de servicios panel) además de habilitar el Desactivar el caché casilla de verificación en el Red panel para deshabilitar también la memoria caché. Para cubrir más navegadores, optamos por una solución diferente al incluir una marca para deshabilitar el almacenamiento en caché en nuestro trabajador de servicio, que está habilitado de forma predeterminada en las compilaciones de desarrolladores. Esto asegura que los desarrolladores siempre obtengan sus cambios más recientes sin problemas de almacenamiento en caché. Es importante incluir el Cache-Control: no-cache encabezado también para evitar que el navegador almacene en caché cualquier activo.

Faro

Faro proporciona una serie de herramientas de depuración útiles para las PWA. Escanea un sitio y genera informes que cubren PWA, rendimiento, accesibilidad, SEO y otras mejores prácticas.
Nosotros recomendamos ejecutando Lighthouse en integración continua para avisarle si no cumple con alguno de los criterios para ser PWA. En realidad, esto nos sucedió una vez, donde el trabajador del servicio no estaba instalando y no nos dimos cuenta antes de un impulso de producción. Tener Lighthouse como parte de nuestro CI lo habría evitado.

Adopte la entrega continua

Debido a que los trabajadores del servicio pueden actualizar automáticamente, los usuarios carecen de la capacidad de limitar las actualizaciones. Esto reduce significativamente la cantidad de clientes desactualizados en la naturaleza. Cuando el usuario abría nuestra aplicación, el trabajador del servicio servía al cliente anterior mientras descargaba el nuevo cliente con pereza. Una vez que el nuevo cliente se descargó, le pedirá al usuario que actualice la página para acceder a nuevas funciones. Incluso si el usuario ignoraba esta solicitud, la próxima vez que actualizara la página, recibiría la nueva versión del cliente. Como resultado, es bastante difícil para un usuario rechazar las actualizaciones de la misma manera que puede hacerlo para las aplicaciones nativas.

Pudimos impulsar cambios importantes en el backend con un tiempo de migración muy corto para los clientes. Por lo general, damos un mes para que los usuarios se actualicen a clientes más nuevos antes de realizar cambios importantes. Dado que la aplicación funcionaría mientras estaba obsoleta, era posible que los clientes más antiguos existieran en la naturaleza si el usuario no había abierto la aplicación durante mucho tiempo. En iOS, los trabajadores del servicio son
desalojado después de un par de semanas
por lo que este caso no sucede. Para Android, este problema podría mitigarse si no se publica mientras está obsoleto o si el contenido caduca manualmente después de algunas semanas. En la práctica, nunca encontramos problemas con clientes obsoletos. Cuán estricto quiere ser un equipo dado aquí depende de su caso de uso específico, pero las PWA brindan una flexibilidad significativamente mayor que las aplicaciones nativas.

Obtener valores de cookies en un trabajador de servicios

A veces es necesario acceder a los valores de las cookies en el contexto de un trabajador de servicios. En nuestro caso, necesitábamos acceder a los valores de las cookies para generar un token para autenticar las solicitudes de API de origen. En un trabajador de servicios, las API síncronas como document.cookies no están disponibles. Siempre puede enviar un mensaje a los clientes activos (con ventana) desde el trabajador del servicio para solicitar los valores de las cookies, aunque es posible que el trabajador del servicio se ejecute en segundo plano sin ningún cliente con ventana disponible, como durante una sincronización en segundo plano. Para solucionar esto, creamos un punto final en nuestro servidor frontend que simplemente devolvía el valor de la cookie al cliente. El trabajador del servicio realizó una solicitud de red a este punto final y leyó la respuesta para obtener los valores de las cookies.

Con el lanzamiento de la
API de la tienda de cookies, esta solución alternativa ya no debería ser necesaria para los navegadores que la admiten, ya que proporciona acceso asincrónico a las cookies del navegador y puede ser utilizada directamente por el trabajador del servicio.

Escollos para los trabajadores de servicios no generados

Asegúrese de que la secuencia de comandos del trabajador del servicio cambie si cambia algún archivo en caché estático

Un patrón de PWA común es que un trabajador del servicio instale todos los archivos de aplicaciones estáticas durante su
install fase, que permite a los clientes acceder directamente a la caché de la API de almacenamiento en caché para todas las visitas posteriores. Los trabajadores del servicio solo se instalan cuando el navegador detecta que la secuencia de comandos del trabajador del servicio ha cambiado de alguna manera, por lo que teníamos que asegurarnos de que el archivo de la secuencia de comandos del trabajador del servicio cambiara de alguna manera cuando cambiaba un archivo en caché. Hicimos esto manualmente al incrustar un hash del conjunto de archivos de recursos estáticos dentro de nuestro script de trabajador de servicio, por lo que cada versión produjo un archivo JavaScript de trabajador de servicio distinto. Bibliotecas de trabajadores de servicios como
Neceser de costura Automatice este proceso por usted.

Examen de la unidad

Las API de trabajador de servicio funcionan agregando detectores de eventos al objeto global. Por ejemplo:

self.addEventListener('fetch', (evt) => evt.respondWith(fetch('/foo')));

Esto puede ser difícil de probar porque necesita simular el disparador del evento, el objeto del evento, esperar el respondWith() devolución de llamada, y luego esperar la promesa, antes de finalmente afirmar el resultado. Una forma más sencilla de estructurar esto es delegar toda la implementación a otro archivo, que se prueba más fácilmente.

import fetchHandler from './fetch_handler.js';
self.addEventListener('fetch', (evt) => evt.respondWith(fetchHandler(evt)));

Debido a las dificultades de la prueba unitaria de un script de trabajador de servicio, mantuvimos el script de trabajador de servicio central lo más básico posible, dividiendo la mayor parte de la implementación en otros módulos. Dado que esos archivos eran solo módulos JS estándar, podrían probarse unitariamente más fácilmente con bibliotecas de prueba estándar.

Estén atentos para las partes 2 y 3

En las partes 2 y 3 de esta serie hablaremos sobre la gestión de medios y problemas específicos de iOS. Si desea preguntarnos más sobre la creación de una PWA en Google, visite nuestros perfiles de autor para averiguar cómo contactarnos:

error: Atención: Contenido protegido.