web

Crear y publicar Progressive Web App – Convertir app web en PWA

Hoy voy a enseñarte cómo convertir cualquier aplicación web en una PWA o Progressive Web App.

Tú puedes programar en cualquier lenguaje y Framework, ya que las PWA no están atadas a algo más allá de JavaScript.

Así que mientras tu app conste de archivos del lado del cliente, podrás convertirla en PWA. Y con estos archivos me refiero a CSS, JS, HTML, imágenes, etcétera.

Te repito que para crear una PWA no necesitas usar un framework específico, así que puedes usar JavaScript puro, Angular, Vue, React, jQuery (bueno, jQuery no, ya es obsoleto) y cualquier otro que genere JavaScript al final.

¿Qué son las PWA?

Si quieres leer teoría, ve a la Wikipedia o busca en Google.

Yo uso las PWA porque se pueden instalar en el dispositivo como si fueran nativas, permiten agregar nuestros archivos al caché y básicamente nos permiten hacer apps “nativas” desde la web usando cualquier framework.

De este modo le das al usuario la posibilidad de instalar tu app y trabajar fuera de línea, así que él puede visitar tu app web una sola vez e instalar la app nativamente en Android, iOS o en la computadora con Windows, MacOS o Linux.

Limitaciones

Veo las PWA como un estándar, y no como una magia. Básicamente una Progressive Web App te permitirá, entre otras cosas, trabajar sin conexión e instalar tu webapp como si fuera nativa; pero no es magia.

Con esto me refiero a que si quieres trabajar sin conexión tú debes implementar esa lógica. Si usas un lenguaje del servidor o una API y el usuario no tiene internet, obviamente no va a poder usar tu app aunque la misma sea una PWA.

Las PWA solo guardan en caché los assets, scripts, estilos, etcétera; pero no guardan una porción de internet.

Además, no todas las apps usan conexión a internet o guardan datos, pero si quieres trabajar sin conexión puedes usar Firebase, PouchDB o localStorage.

Generando manifesto

Hasta este punto y para los siguientes voy a suponer que ya tienes tu app final que solo consta de archivos web del lado del cliente.

Bien, comenzamos creando el manifiesto y colocándolo junto a tus assets para luego importarlo en el HTML. Siéntete libre de modificarlo y adaptarlo a tus necesidades:

{
    "name": "Deudas",
    "short_name": "Deudas",
    "theme_color": "#2196f3",
    "background_color": "#2196f3",
    "display": "fullscreen",
    "orientation": "portrait",
    "scope": "./",
    "start_url": "./",
    "icons": [
        {
            "src": "./images/icono-512.png",
            "type": "image/png",
            "sizes": "512x512"
        }
    ]
}

En este caso icono-512 es la imagen del tamaño 512×512. Se recomienda que las imágenes sean cuadradas. Obviamente el src puede cambiar dependiendo de tu proyecto. Puedes agregar varias imágenes para distintos tamaños.

Después importamos el manifiesto en el index.html, obviamente si tú cambiaste la ruta entonces cambia lo necesario:

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

Luego vamos a regresar al index.html a registrar el Service Worker, pero primero debemos crearlo.

Instalando workbox

Workbox es una herramienta que te va a permitir generar el Service Worker automáticamente. Tú puedes generarlo a mano, pero yo recomiendo siempre usar herramientas de automatización.

No es nada difícil, ya lo verás. Solo necesitas NPM y Node. Una vez que cuentes con Node y NPM ejecuta lo siguiente para instalarlo:

npm install workbox-cli --global

Y espera a que se instale. Luego de eso, reinicia todas las terminales que tengas abiertas. Por cierto, para los siguientes pasos voy a ejecutar los comandos en la ubicación donde tengo mi proyecto.

Generando archivo de configuración para service worker

Ya tenemos workbox, ahora vamos a generar un archivo de configuración. Este archivo le dice a workbox cómo debe crear nuestro service worker. Para ello vamos a invocar un asistente.

En mi caso tengo mi proyecto con la CLI de Vue, y al compilar me genera el JS, CSS y HTML en la carpeta dist, por ello es que en los siguientes pasos le diré al asistente que mi proyecto está en dist.

Si tú tienes otra configuración o carpeta, entonces muévete a esa ruta antes de seguir los pasos. Pero bueno, comenzamos ejecutando:

workbox wizard

Elegimos el directorio, en mi caso es dist. Luego marcamos los archivos que queremos cachear, podemos marcar y desmarcar con la tecla de espacio.

Yo dejé todas marcadas y presioné Enter. Cuando me preguntó en dónde guardar el archivo de service-worker acepté la ruta, que fue en la carpeta dist/sw.js.

Nota: una cosa es el archivo de configuración, y otra cosa es el service worker. Workbox lee el archivo de configuración y genera el service worker.

Por otro lado, las configuraciones las guardé en la carpeta padre de dist, tal y como el asistente me lo sugirió.

Cuando me preguntó si quería eso de los parámetros de búsqueda le di N. A ti te puede preguntar otras cosas en el futuro, solo tienes que leer y responder. Luego me dijo:

To build your service worker, run

  workbox generateSW workbox-config.js

as part of a build process. See https://goo.gl/fdTQBf for details.
You can further customize your service worker by making changes to workbox-config.js. See https://goo.gl/gVo87N for details.

Llegados a este punto ya podemos generar nuestro service worker.

Generando service worker

Tal como lo dijo la salida, ahora solo debemos ejecutar: workbox generateSW workbox-config.js. A ti te puede dar otra salida obviamente.

Con esto le indicamos a workbox que genere el service worker tomando en cuenta el archivo workbox-config.js, que ya tiene los ajustes que pusimos anteriormente y que podemos modificar a nuestro gusto.

Nota: al ejecutarlo, me generó 4 archivos, pero los .map no son necesarios. Así que solo toma los 2 js, que son sw.js y workbox-letras-y-números.js

Siempre que ejecutes el comando, se va a crear el service worker. Es recomendable ejecutarlo siempre que hagas cambios o cambies archivos.

En mi caso ejecuto npm run build para compilar mi app, misma que me genera el directorio dist. E inmediatamente después de eso ejecuto workbox generateSW workbox-config.js para que genere el service worker.

Nota: cada que hagas un cambio debes generar el ServiceWorker de nuevo. Presta atención; dije Service Worker, y no archivo de configuración, pues el archivo de configuración se debe generar una vez por proyecto.

Volviendo al index.html

Ya tenemos nuestro ServiceWorker que en mi caso se ve así:

if(!self.define){let e,c={};const i=(i,n)=>(i=new URL(i+".js",n).href,c[i]||new Promise((c=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=c,document.head.appendChild(e)}else e=i,importScripts(i),c()})).then((()=>{let e=c[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e})));self.define=(n,s)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(c[o])return;let r={};const t=e=>i(e,o),f={module:{uri:o},exports:r,require:t};c[o]=Promise.all(n.map((e=>f[e]||t(e)))).then((e=>(s(...e),r)))}}define(["./workbox-ac6e2db0"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.precacheAndRoute([{url:"css/chunk-vendors.0af22a31.css",revision:"e2e858b4d120eb644b8259240d7ec8d9"},{url:"favicon.ico",revision:"1dc6fa57e5d5fbca02aad225ca0f9e1c"},{url:"fonts/materialdesignicons-webfont.9cacdc87.eot",revision:"9cacdc876e2049988fcab540c21738d5"},{url:"fonts/materialdesignicons-webfont.9d243c16.woff2",revision:"9d243c168a4f1c2cb3cec74884344de7"},{url:"fonts/materialdesignicons-webfont.a0711490.woff",revision:"a0711490bcd581b647329230b3e915cf"},{url:"fonts/materialdesignicons-webfont.b62641af.ttf",revision:"b62641afc9ab487008e996a5c5865e56"},{url:"images/icono-512.png",revision:"785dd4631c1bcc23a9bc4c253531a688"},{url:"index.html",revision:"e739386a271e6fa79521075247284d79"},{url:"js/app.47c9b442.js",revision:"1e44598bc22c04452f367b89706f60ee"},{url:"js/chunk-vendors.fc19b828.js",revision:"2fdc8172c8f00b6ba891a822bd55cd6d"},{url:"manifest.json",revision:"062998d4565fc1a85f585d673c08aeea"}],{ignoreURLParametersMatching:[/^utm_/,/^fbclid$/]})}));
//# sourceMappingURL=sw.js.map

También es importante copiar el archivo workbox-letras-y-números.js que Workbox generó, y este archivo debe estar en el mismo directorio que el sw.js.

Vamos al index.html, en el head registramos el service worker (recuerda que igualmente ahí debemos importar el manifiesto):

<script type="text/javascript">
  if ("serviceWorker" in navigator) {
    navigator.serviceWorker.register("sw.js");
  }
</script>

Obviamente sw.js está en la misma carpeta que mi index.html, así que por eso lo registro de esa manera. Si el tuyo está en otra ubicación o tiene otro nombre cambia lo que sea necesario.

Verificando PWA

Entonces ya tenemos toda nuestra app, el manifiesto y el service worker. En mi caso se ve así:

Archivos para publicar Progressive Web App

Sí, ya sé que se me pasaron los .map pero en un momento los eliminaré. El punto es que esa es mi webapp completa, ya lista para enviarla a producción.

Es momento de subirla a un servidor HTTPS, y si no tienes ninguno puedes usar las Github pages, ya que al final solo estamos usando archivos del lado del cliente.

Una vez subida navega hasta tu app web recién publicada, abre la consola de depuración y ve a Application > Manifest, verás que tu manifiesto es válido y se debe ver algo así:

Verificar manifiesto de PWA

Luego en Service Workers también podrás ver que el archivo generador por workbox ha funcionado:

Service worker en Progressive Web App

Llegados a este punto ya puedes instalar tu web app progresiva como si fuera nativa, ya sea en la computadora o en un móvil. Así se ve en mi teléfono con Android, lista para ser instalada:

Instalar PWA como nativa en Android desde Google Chrome

Además, si el usuario visita nuestra app progresiva varias veces, aparece la sugerencia para instalar en la parte inferior. Y así es como podemos crear una PWA con nuestros archivos web, sin importar el framework o tecnologías usadas.

Posible error

En julio de 2023 intenté usar estos comandos y me aparecía el siguiente error:

Please pass in a valid CommonJS module that exports your configuration.

 

require() of ES Module C:\Users\parzibyte\Documents\desarrollo\svelte\notas_sqlite_svelte\workbox-config.js from C:\Users\parzibyte\AppData\Roaming\npm\node_modules\workbox-cli\build\lib\read-config.js not supported.
workbox-config.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains “type”: “module” which declares all .js files in that package scope as ES modules.

Instead rename workbox-config.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change “type”: “module” to “type”: “commonjs” in C:\Users\parzibyte\Documents\desarrollo\svelte\notas_sqlite_svelte\package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

Me imagino que es debido a la versión de Node y NPM que tengo. Por si sirve, mis versiones son:

  1. Node: v18.16.0
  2. npm: 9.6.7

Lo solucioné renombrando el workbox-config.js a workbox-config.cjs.

Bonus: creando PWA con Vue 3

En estos días he retomado este tutorial y lo he ajustado para enseñarte cómo convertir una webapp de Vue 3 a PWA:

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

  • Estoy realizando una pwa de ejemplo para aprender como funcionan.
    He seguido todos los pasos pero no me reconoce el service worker en el navegador.
    Se ha generado correctamente dentro de mis directorios, pero a la hora de ver si mi web se ha convertido en pwa solo reconoce el manifest, estando el sw registrado en la cabecera de mis archivos .html, alguna idea de que puede fallar?

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

3 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

3 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

3 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

3 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

3 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

3 días hace

Esta web usa cookies.