En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que se componga de CSS, JavaScript y HTML se puede convertir en una PWA, y ese también es el caso con Vue 3.
Aunque Vue 3 con Vite compila a CSS, HTML y JS que podemos convertir a PWA quise darme a la tarea de documentar el proceso de creación. Por favor toma en cuenta que te recomiendo revisar el siguiente artículo para saber lo básico sobre las PWA:
Crear y publicar Progressive Web App – Convertir app web en PWA
Veamos entonces cómo convertir una aplicación de Vue 3 con Vite en una PWA.
Aplicación de ejemplo
Este artículo fue usado para convertir una aplicación de Vue 3 en PWA. La PWA en cuestión es un montador de imágenes en lote que puedes usar en el siguiente enlace para comprobar que es una PWA en toda regla:
Creador de credenciales y etiquetas escolares
Esta no es la primera vez que convierto una app de Vue 3 en PWA, pues también lo hice con el Diseñador para impresoras térmicas.
Crear manifest.json agregando iconos y capturas de pantalla
Comencemos creando el manifest.json
en la carpeta public
. Su contenido queda así:
{
"name": "Card ID creator",
"description": "Batch card and ID creation",
"short_name": "Card creator",
"theme_color": "#2196f3",
"background_color": "#2196f3",
"display": "standalone",
"orientation": "portrait",
"scope": "./",
"start_url": "./",
"icons": [
{
"src": "./images/icono-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"screenshots": [
{
"src": "./images/screenshot.png",
"type": "image/png",
"sizes": "1920x1014",
"form_factor": "wide"
},
{
"src": "./images/screenshot.png",
"type": "image/png",
"sizes": "1920x1014"
}
]
}
En esa misma carpeta public
coloqué las imágenes (screenshot.png
e icono-512.png
) dentro de images
. Luego hay que importar ese manifiesto en el index.html
:
<link rel="manifest" href="./manifest.json">
Puedes aprender más sobre el formato del archivo de manifiesto en este enlace: https://web.dev/articles/add-manifest?hl=es
Creando service worker
Vamos a necesitar Comlink. Yo ejecuté workbox wizard
y le dije que la carpeta que hay que convertir a PWA es dist
, pues más adelante cuando se compile la app de Vue 3 con npm run build
todo el resultado se colocará en dist
.
Cuando el asistente terminó, generó un archivo llamado workbox-config.js
que tuve que renombrar a workbox-config.cjs
en la carpeta raíz porque si no lo renombraba me daba algunos errores.
Mi workbox-config.cjs
ya completo queda como se ve a continuación:
module.exports = {
globDirectory: 'dist/',
globPatterns: [
'**/*.{css,js,wasm,ico,html,png,jpg,json,svg}'
],
swDest: 'dist/sw.js',
ignoreURLParametersMatching: [
/^utm_/,
/^fbclid$/
],
maximumFileSizeToCacheInBytes: 5000000,
sourcemap: false,
};
Fíjate que swDest
es el destino del service worker. Con la configuración actual, se va a crear un archivo llamado sw.js
que después debemos importar en el index.html
.
Modifiqué la propiedad maximumFileSizeToCacheInBytes
porque tenía un archivo wasm que era muy pesado, y también modifiqué sourcemap
porque no quiero archivos map.
Importando service worker en index.html
Hasta este punto ya existe:
- Configuración de Workbox en
workbox-config.cjs
- El manifiesto en
manifest.json
- La importación de ese manifiesto en el
index.html
Ahora falta agregar la importación del service worker en el index.html
:
<script type="text/javascript">
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("sw.js");
}
</script>
Aquí estamos suponiendo que sw.js
va a ser hermano de index.html
. En modo desarrollo nos va a mostrar un error porque sw.js
no va a existir, pero cuando compilemos la app con npm run build
y tanto el index.html
como el sw.js
estén en dist
entonces todo va a funcionar.
Solo como referencia, aquí dejo mi index.html
completo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Card creator - By Parzibyte</title>
<link rel="manifest" href="./manifest.json">
<script type="text/javascript">
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("sw.js");
}
</script>
<link rel="icon" type="image/png" href="./favicon-48x48.png" sizes="48x48" />
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
<link rel="shortcut icon" href="./favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Compilando aplicación de Vue 3
Recapitulemos. Hasta este momento ya tenemos:
- Configuración de Workbox para generar el Service Worker y colocarlo en la carpeta
dist
- Manifiesto para la PWA en
manifest.json
- Importación del manifiesto e instalación del ServiceWorker en el
index.html
Ahora solo falta crear la carpeta dist
, y eso lo hará automáticamente el comando npm run build
. Así que ejecutamos:
npm run build
Y eso creará la carpeta dist
con el siguiente contenido:
total 388
-rw-r--r-- 1 parzibyte parzibyte 44630 oct. 23 03:38 apple-touch-icon.png
drwxr-xr-x 1 parzibyte parzibyte 0 nov. 5 11:03 assets
-rw-r--r-- 1 parzibyte parzibyte 15086 oct. 23 03:38 favicon.ico
-rw-r--r-- 1 parzibyte parzibyte 313683 oct. 23 03:38 favicon.svg
-rw-r--r-- 1 parzibyte parzibyte 4751 oct. 23 03:38 favicon-48x48.png
drwxr-xr-x 1 parzibyte parzibyte 0 nov. 5 11:03 images
-rw-r--r-- 1 parzibyte parzibyte 896 nov. 5 11:03 index.html
-rw-r--r-- 1 parzibyte parzibyte 792 oct. 22 21:36 manifest.json
Fíjate en que el index.html
y el manifest.json
son hermanos, así como la carpeta images
y todo lo demás, pero todavía nos falta el sw.js
así que vamos al último paso.
Generar Service Worker con Workbox después de compilar
Ahora que la carpeta dist
ya existe podemos ejecutar workbox generateSW workbox-config.cjs
en donde workbox-config.cjs
es el archivo que el wizard ha creado por nosotros.
Una vez ejecutado nos va a aparecer una salida como la siguiente:
C:\xampp\htdocs\credenciales>workbox generateSW workbox-config.cjs
Using configuration from C:/xampp/htdocs/credenciales/workbox-config.cjs.
Browserslist: caniuse-lite is outdated. Please run:
npx update-browserslist-db@latest
Why you should do it regularly: https://github.com/browserslist/update-db#readme
The service worker files were written to:
• C:/xampp/htdocs/credenciales/dist/sw.js
• C:/xampp/htdocs/credenciales/dist/workbox-ea0bf025.js
The service worker will precache 15 URLs, totaling 7.15 MB.
Lo importante es el archivo sw.js
y workbox-ea0bf025.js
(mismo que puede cambiar su nombre en cada generación). Esos archivos fueron copiados a la carpeta dist
automáticamente o mejor dicho fueron generados dentro de esa carpeta.
Ahora veamos de nuevo el contenido de dist
:
total 408
-rw-r--r-- 1 parzibyte parzibyte 44630 oct. 23 03:38 apple-touch-icon.png
drwxr-xr-x 1 parzibyte parzibyte 0 nov. 5 11:03 assets
-rw-r--r-- 1 parzibyte parzibyte 15086 oct. 23 03:38 favicon.ico
-rw-r--r-- 1 parzibyte parzibyte 313683 oct. 23 03:38 favicon.svg
-rw-r--r-- 1 parzibyte parzibyte 4751 oct. 23 03:38 favicon-48x48.png
drwxr-xr-x 1 parzibyte parzibyte 0 nov. 5 11:03 images
-rw-r--r-- 1 parzibyte parzibyte 896 nov. 5 11:03 index.html
-rw-r--r-- 1 parzibyte parzibyte 792 oct. 22 21:36 manifest.json
-rw-r--r-- 1 parzibyte parzibyte 1961 nov. 5 11:06 sw.js
-rw-r--r-- 1 parzibyte parzibyte 14273 nov. 5 11:06 workbox-ea0bf025.js
Los archivos fueron agregados correctamente. Y ahora sí ya podemos distribuir el contenido de dist, pues ya es una PWA.
Compilando más adelante
La configuración de Workbox para generar el workbox-config.cjs
, la importación del sw.js
, la importación del manifiest.json
y la creación del manifiesto solo se hace una vez. Después, cada vez que hagas cambios a tu aplicación web con Vue 3 y quieras convertirla a PWA ejecuta:
npm run build
Luego:
workbox generateSW workbox-config.cjs
Y distribuye todo lo que existe dentro de dist
. Con “distribuir” me refiero a subir el contenido de dist a un servidor local o de internet que pueda servir archivos estáticos.
Distribuir aplicación
Puedes alojar la PWA en cualquier lugar, incluso en las GitHub pages. Yo tengo un tutorial para desplegarla en un servidor con Apache.