Arreglamos un bug de layout en el calendario de Formi. Lo verificamos en local, contra el build de producción, con Playwright apuntando al sitio real — perfecto en los tres. Lo subimos.
El usuario que reportó el bug lo siguió viendo. Exactamente igual.
No era caché del CDN, no eran los hashes de los archivos (Vite les pone un hash distinto en cada build, eso ya estaba bien resuelto). El problema era más aburrido y más traicionero: tenía la app instalada como PWA, y una PWA instalada no se comporta como una pestaña del navegador — nadie la "refresca". El usuario la abre desde el ícono, ve la última versión que el Service Worker tiene cacheada, y listo. Si ese Service Worker nunca se entera de que hay uno nuevo, la app queda congelada en el tiempo — para siempre, hasta que alguien la desinstale y reinstale a mano.
Por qué pasa esto
Un Service Worker no se actualiza solo por arte de magia. El navegador lo chequea en background en algunos casos (navegación completa, cierre y reapertura de pestaña), pero una PWA que vive en la pantalla de inicio y se abre/cierra todo el tiempo sin recargar nunca dispara esos chequeos. Necesitás pedírselo vos, explícitamente, en el momento correcto.
La solución (3 piezas)
1. Versionar el Service Worker en cada build, no solo tus assets:
// vite-plugin-sw-version.js — corre en cada `vite build`
const version = `myapp-${Date.now()}`;
// reemplaza un placeholder en sw.js por este string único
Sin esto, aunque el navegador chequee, va a ver "el mismo" sw.js de siempre y no va a descargar nada nuevo.
2. Pedirle al navegador que chequee, activamente, cada vez que la app vuelve a primer plano:
const checkForUpdate = () => reg.update().catch(() => {});
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') checkForUpdate();
});
window.addEventListener('focus', checkForUpdate);
3. Cuando encuentra una versión nueva, que tome control de inmediato y recargue solo (en vez de esperar a que el usuario cierre todas las pestañas, que en una PWA instalada puede no pasar nunca):
// en sw.js: self.skipWaiting() en 'install', self.clients.claim() en 'activate'
// del lado del cliente:
navigator.serviceWorker.addEventListener('controllerchange', () => {
window.location.reload();
});
El detalle molesto
Si tu PWA ya está instalada en el celular de alguien sin este mecanismo, necesita reinstalarse una única vez para que el mecanismo nuevo entre en juego. Es la única vez que vas a tener que pedirle a un usuario que desinstale/reinstale — de ahí en adelante, cada deploy se actualiza solo.
Probalo
Armé las tres piezas como paquete de npm, sin dependencias, funciona con Vite + cualquier framework:
npm install pwa-auto-update
Código y detalles: github.com/ivoangaramo-ux/formi-oss/tree/main/pwa-auto-update
Top comments (0)