-
-
Notifications
You must be signed in to change notification settings - Fork 201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bust cache after a release? #33
Comments
Hi, There is a pending think to do about refresh on new content detected. You can use register-service-worker, and do a manual installation of service worker via
import type { Ref } from 'vue'
import { register } from 'register-service-worker'
import { ref } from 'vue'
// https://medium.com/google-developer-experts/workbox-4-implementing-refresh-to-update-version-flow-using-the-workbox-window-module-41284967e79c
// https://github.com/yyx990803/register-service-worker
// https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading
export const useServiceWorker = (): {
appNeedsRefresh: Ref<boolean>
offlineAppReady: Ref<boolean>
} => {
const offlineAppReady = ref(false)
const appNeedsRefresh = ref(false)
register(
'/sw.js',
{
registrationOptions: { scope: '/' },
updated: async() => { appNeedsRefresh.value = true },
cached: () => { offlineAppReady.value = true },
},
)
return {
appNeedsRefresh,
offlineAppReady,
}
} then on your const {
appNeedsRefresh,
offlineAppReady,
} = useServiceWorker()
watch(appNeedsRefresh, async() => {
window.location.reload()
}, { immediate: true }) obviously, you will need to add some button on the screen, then show/enable it using |
Thanks, that looks like a reasonable solution. I'll keep this ticket open until I implement this properly :) |
@antfu As soon I have time I'll try to fix it using workbox instead register-service-worker, I'm very busy at work... In the meantime this is the approach I'm using on my projects with vitesse template. |
@antfu I have a problem about using
Then, the problem arises, I need to include To put you in context: My first attempt was to configure my pwa like this ( VitePWA({
...
workbox: {
cleanupOutdatedCaches: true,
skipWaiting: true,
clientsClaim: true,
}
}),
... This aproach has a problem, the pages remains on the cache, also after a refresh and my pages stop working, because I removed the old registered service worker ( My second attemp was just removing The problem with activating The solution described in Offer a page reload for users seems to work, but I haven't test it yet. In this link you can find the problem and how can be solved, in fact it points to the Offer a page reload for users, just read it. We can also find a warning in workbox using |
Thanks a lot for the detailed info!
This looks like a good solution to me! We can make an option for people to opt-in with this behavior.
Do we need these tho? |
No, just to remove both and do it manually with the |
this script will replace manual registration: what this plugin currently does will be replace with the script in |
What I had in mind was to include And then, here is the problem again (mixing build time and runtime)... |
I think maybe we don't need to be Vue specific (this plugin is framework-agnostic). We could update this https://github.com/antfu/vite-plugin-pwa/blob/c2054640b1a81650a8172e1fe1dadb28178e4957/src/html.ts#L9-L16 to put the snippet from After that, we can provide a minimal example of how to set it up for people to explore. (and Vitesse as well for sure) |
The problem with this approach is that is not tied to the app ui, that is, we need to add a callback, for example, with my first approach using I think it would be nice to add the |
As you can see the problem is mixing again buildtime and runtime, we need to interact with the ui to activated the service worker. |
My suggestion is to include a new |
I am thinking about we can serve the register script in a virtual module, so the usage would be like this, where they can be bundled with their UI logics // virtual module
import registerSW from 'vite-plugin-pwa-register'
const sw = registerSW({
onNeedRefresh() {
// show an UI for user to refresh
}
}) |
wait to @scambier response, I think it must work (if not using |
The code you proposed actually doesn't solve my issue it seems. In the end it just triggers a |
@scambier yes, this is the problem I have, but with my SPA acting as a MPA (my server build route pages instead just returning/forwarding to index.html), and I havenn't tested on a real SPA, your answer confirms my suspicions... can you try this one (just change the callback)?: updated: async(registration) => {
registration.update()
appNeedsRefresh.value = true
}, @antfu try to make a branch where we can test it before merging into master, the problem is I haven't time... |
upps, add |
@scambier the problem is that the service worker is installed but not activated, we need to provide a callback to the service worker to control the opened pages/tabs: the lifecycle of the service worker is a little complicated. What I'm talking with @antfu is to try to activate the service worker once is updated, but we need to change the code. For example, vuejs 3 (using workbox 4, here we are using 6.1.1), has some code, I'll try to investigate, to activate and claim clients to take control of opened windows/tabs controlled by the service worker (I need to see where attaching the listener to show the dialog for
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
addEventListener('message', event => {
const replyPort = event.ports[0]
const message = event.data
if (replyPort && message && message.type === 'skip-waiting') {
event.waitUntil(
self.skipWaiting().then(
() => replyPort.postMessage({ error: null }),
error => replyPort.postMessage({ error })
)
)
}
}) |
Following the code, it seems using |
This seems to do the job. Here's what I have at the moment: useServiceWorker.ts register(
'/sw.js',
{
registrationOptions: { scope: '/' },
updated: async (registration) => {
await registration.update()
registration.unregister()
appNeedsRefresh.value = true
},
cached: () => {
offlineAppReady.value = true
}
}
) main.ts const { appNeedsRefresh, offlineAppReady } = useServiceWorker()
watch(appNeedsRefresh, async (val) => {
if (val) {
console.log('app updated and sw unregistered, will refresh')
// TODO: prompt user
location.reload()
}
}, { immediate: true }) There's just a point I'm missing. In your first response, you wrote
But I'm not quite sure which setting to change. Edit: also, I guess unregistering the worker before showing the prompt isn't really a good idea, but I'll change that later. For now I just need to properly clean the cache after an update :) |
VitePWA({
injectRegister: null,
}) |
to clear the cache use this:
|
if you don't have Setting And please, confirm that update call just works or adding unregister. |
Testing on my MPA it seems we need to unregister first, then update and then refresh page, but still service worker not controlling page/tabs. About the cache @scambier see screenshot below. I'm checking if @antfu With this approach there is no need to modify code, just wait until my test can confirm that works... |
Ok, the app correctly cleans the cache after a new deployment, but only if I refresh or open a new tab. And in that case, it only reloads the first tab. The To recap, here's what I currently have. useServiceWorker.ts: import type { Ref } from 'vue'
import { register } from 'register-service-worker'
import { ref } from 'vue'
// https://medium.com/google-developer-experts/workbox-4-implementing-refresh-to-update-version-flow-using-the-workbox-window-module-41284967e79c
// https://github.com/yyx990803/register-service-worker
// https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading
export const useServiceWorker = (): {
appNeedsRefresh: Ref<boolean>
offlineAppReady: Ref<boolean>
} => {
const offlineAppReady = ref(false)
const appNeedsRefresh = ref(false)
register(
'/sw.js',
{
registrationOptions: { scope: '/' },
updated: async (registration) => {
await registration.update()
registration.unregister()
appNeedsRefresh.value = true
},
cached: () => {
offlineAppReady.value = true
}
}
)
return {
appNeedsRefresh,
offlineAppReady
}
} main.ts (after const { appNeedsRefresh } = useServiceWorker()
watch(appNeedsRefresh, async (val) => {
console.log('Does app need refresh? ' + val)
if (val) {
// if (confirm('App updated. Do you want to refresh?')) {
location.reload()
// }
}
}, { immediate: true }) PWA config in vite.config.ts: VitePWA({
injectRegister: null,
manifest: {
/* */
},
workbox: {
cleanupOutdatedCaches: true
}
}) Thanks a lot for your explanations. I'll take time to read some more doc about service workers next week :) |
I'm trying to simulate Workbox approach, just copy/paste |
@antfu confirmed that works at least on my MPA with the Just update the imports for The usage is calling import type { Ref } from 'vue'
import { Workbox } from '/~/logics/service-worker/Workbox'
import { messageSW } from '/~/logics/service-worker/messageSW'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
export const useServiceWorker = (immediate = false): {
offlineAppReady: Ref<boolean>
appNeedsRefresh: Ref<boolean>
updateServiceWorker: () => Promise<void>
} => {
const offlineAppReady = ref(false)
const appNeedsRefresh = ref(false)
const router = useRouter()
let registration: ServiceWorkerRegistration
let wb: Workbox
const updateServiceWorker = async() => {
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
wb.addEventListener('controlling', (event) => {
if (event.isUpdate)
window.location.reload()
})
if (registration && registration.waiting) {
// Send a message to the waiting service worker,
// instructing it to activate.
// Note: for this to work, you have to add a message
// listener in your service worker. See below.
await messageSW(registration.waiting, { type: 'SKIP_WAITING' })
}
}
router.isReady().then(() => {
if ('serviceWorker' in navigator) {
wb = new Workbox('/sw.js', { scope: '/' })
const showSkipWaitingPrompt = () => {
// `event.wasWaitingBeforeRegister` will be false if this is
// the first time the updated service worker is waiting.
// When `event.wasWaitingBeforeRegister` is true, a previously
// updated service worker is still waiting.
// You may want to customize the UI prompt accordingly.
// Assumes your app has some sort of prompt UI element
// that a user can either accept or reject.
appNeedsRefresh.value = true
}
wb.addEventListener('controlling', (event) => {
if (!event.isUpdate)
offlineAppReady.value = true
})
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', showSkipWaitingPrompt)
// @ts-ignore
wb.addEventListener('externalwaiting', showSkipWaitingPrompt)
wb.register({ immediate }).then(r => registration = r!)
}
})
return {
offlineAppReady,
appNeedsRefresh,
updateServiceWorker,
}
} |
THIS WILL NOT WORK, SO FORGET IT: seems we need to register controlling event to the right registration (this approach will register on old sw) SO USE MODULE FROM PREVIOUS COMMENT. after a revision: import type { Ref } from 'vue'
import { Workbox } from '/~/logics/service-worker/Workbox'
import { messageSW } from '/~/logics/service-worker/messageSW'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
export const useServiceWorker = (immediate = false): {
offlineAppReady: Ref<boolean>
appNeedsRefresh: Ref<boolean>
updateServiceWorker: () => Promise<void>
} => {
const offlineAppReady = ref(false)
const appNeedsRefresh = ref(false)
const router = useRouter()
let registration: ServiceWorkerRegistration
const updateServiceWorker = async() => {
if (registration && registration.waiting) {
// Send a message to the waiting service worker,
// instructing it to activate.
// Note: for this to work, you have to add a message
// listener in your service worker. See below.
await messageSW(registration.waiting, { type: 'SKIP_WAITING' })
}
}
router.isReady().then(() => {
if ('serviceWorker' in navigator) {
const wb = new Workbox('/sw.js', { scope: '/' })
const showSkipWaitingPrompt = () => {
// `event.wasWaitingBeforeRegister` will be false if this is
// the first time the updated service worker is waiting.
// When `event.wasWaitingBeforeRegister` is true, a previously
// updated service worker is still waiting.
// You may want to customize the UI prompt accordingly.
// Assumes your app has some sort of prompt UI element
// that a user can either accept or reject.
appNeedsRefresh.value = true
}
wb.addEventListener('controlling', (event) => {
// Assuming the user accepted the update, set up a listener
// that will reload the page as soon as the previously waiting
// service worker has taken control.
if (event.isUpdate)
window.location.reload()
else
offlineAppReady.value = true
})
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', showSkipWaitingPrompt)
// @ts-ignore
wb.addEventListener('externalwaiting', showSkipWaitingPrompt)
wb.register({ immediate }).then(r => registration = r!)
}
})
return {
offlineAppReady,
appNeedsRefresh,
updateServiceWorker,
}
} |
also working on my MPA, just configuring The configuration for VitePWA({
injectRegister: null,
manifest: {
/* */
},
workbox: {
cleanupOutdatedCaches: true
}
}) |
No, the issue is being resolved, @antfu has create PR #34 . Once merged on main it will be resolved also this issue (do not close it, github will close automatically). Anyway, read service worker documentation, maybe your app cannot be used with a service worker, you need to review if matches your requisites. For example, my MPA was originally to be 2 native apps to work on android and ios. The resources spent to do this compared to make it a PWA are significant. My decision was to make a PWA and so I need a service worker, it also needs to be able to work offline like natives ones. I have to include in my PWA server push notifications to avoid what you are describing... If your server doesn't have push notifications, either, remove PWA or just make and alternate api entry point to test if there is a new version. For example, make some configuration, a timestamp an internal version number and store it on local storage. You can periodically check it to some api serving a plain number and simulate a refresh. |
I tried to read through the discussion here and I'm afraid I couldn't really digest it all :d So a quick question: Couldn't I just have a If it was allowed it would basically allow the app to function offline if there's no internet connection, but otherwise latest |
@posva welcome to the hell: the main problem is bypass the browser cache and then the service worker cache: the new version will use StaleWhileRevalidate strategy, that means, go to cache and then in background go to server and then update if necessary. If we go NetworkFirst strategy then we don't need the service worker, only for offline requirement. In some situations this will work but as a general solution to speed up with local cache then is not suitable. As I mention above, you will need to check if the app requisites matches the usage of a service worker. In fact, I have some apps that runs below 200ms using server push with esmodules for any page, so if I configure it with NetworkFirst strategy, the service worker will only be necessary if offline is required. About the user click on the button, it is a general adoption, just see vuejs docs, vuetify docs, nuxt docs (vuepress)... I'm talking with @antfu and have included a new |
@posva Creo que hablas español, te lo explico a ver si resuelve tus dudas. El problema con los service workers es que tiene un ciclo de vida bastante entrerrevesado y por eso hay cosas que no encajan o no cuadran bien. El principal problema está en el lapso de tiempo entre que un service worker está esperando a ser activado (skip waiting) y el momento en que se activa (activated). Esto que parece tan simple es un problema ya que provoca que no puedas hacer el bypass de la caché del mismo selectivamente. Mientras un service worker no está activado, entonces es como si no existiese desde el punto de vista del aplicativo. El que incluyas una estrategia u otra no importa, porque lo que no tienes en cuenta es que quien realmente te está retornando lo que solicitas al backend es el que está activo. Cuando has planteado la duda, has obviado este matiz, ya que aunque vayas al backend a por los datos y te retorne los últimos, te va a funcionar en ese instante, pero si haces un F5, te lo va a retornar el service worker activo, recuerda que todavía el nuevo no está activado, con lo que solicitará los assets con la versión anterior y lo que pasará es que se te quedará la pantalla en blanco y si miras la pestaña de red en el devtools, verás 404 de todos los recursos antiguos. Como ves no es que falle en un punto o en otro, es un compendio de cosas, esta incidencia refleja lo que te he descrito y para llegar a ello he estado unos cuantos meses dándoles vueltas a cosas similares a tu planteamiento y que realmente no solucionaban nada. Si estás un poco sorprendido, creo que hago mención a lo que hace Si miras este comentario #34 (comment), se lo explico a Anthony, y aún así no consiguíamos hacer el bypass de la caché del navegador. |
This comment has been minimized.
This comment has been minimized.
Leches pues ni me he dado cuenta, perdona Eduardo por las molestias, juraría que escribí el nombre bien al responder, habrá sido el autocomplete de las narices... como el predictivo del telefono |
Yeah. That would be good enough for me. If I need to pick between fast boot from cache but with occasional manual app refresh via button click or a bit slower app boot (because if online, server is contacted) and keeping the main HTML file up to date automatically, I'd choose the second option. I think the "networkFirst HTML" way still qualifies as PWA. If you generate proper bundles with hashes (as vite does) you can cache those nicely so that still makes the boot much quicker and if you go fully offline, your app still works. I'm looking around and it seems some ppl seem to achieve this via workbox: |
Nuxt seems to also go in this direction: nuxt-community/pwa-module#406 |
@MartinMalinda you can see the new PR #34 using |
I forgot to mention that the current version is using |
@MartinMalinda following your links, I think we have the same behavior described in GoogleChrome/workbox#1767 (comment) What the new PR #34 generates is this: self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
/**
* The precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
workbox.precacheAndRoute([{
"url": "assets/index.40de6d3d.css",
"revision": "0b973314e89c1b1876dfd49f6241cdde"
}, {
"url": "assets/index.946e44f0.js",
"revision": "6c4f2bbf84081cacf649a67aa3f28b72"
}, {
"url": "assets/vendor.35599eb4.js",
"revision": "57c102d464ad82af5c1c300d2de100a0"
}, {
"url": "index.html",
"revision": "f4d735ef53aae1144d2a255bc7d8bf9b"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"))); I will test if enabling self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
}); with: self.skipWaiting();
workbox.clientsClaim(); I need to check if both are equivalent (the change and the link to GoogleChrome). |
@MartinMalinda @antfu it seems to work, also with an F5, no user intertaction. I have a pending thing to do, to check nested routes and other routes than / using vue-router: I need to include |
@MartinMalinda I have on local the VitePWA({
injectRegister: 'networkfirst',
manifest: {
...
},
}) this is what it generates on build (it needs a review, but it works: basically try call // src/client/build/networkfirst.ts
importScripts("/workbox-v6.1.1/workbox-sw.js");
var debug = JSON.parse("false") === true;
var modulePathPrefix = "/workbox-v6.1.1/";
var useModulePathPrefix = modulePathPrefix.startsWith("/") ? modulePathPrefix.substring(1) : modulePathPrefix;
workbox.setConfig({debug, modulePathPrefix});
var modules = [
"workbox-core",
"workbox-routing",
"workbox-cacheable-response",
"workbox-strategies",
"workbox-expiration"
].map((module) => {
workbox.loadModule(module);
return `${useModulePathPrefix}${module}`;
});
var cacheNames = workbox.core.cacheNames;
var {registerRoute, setCatchHandler, setDefaultHandler} = workbox.routing;
var {CacheableResponsePlugin} = workbox.cacheableResponse;
var {
NetworkFirst,
StaleWhileRevalidate,
NetworkOnly
} = workbox.strategies;
var {ExpirationPlugin} = workbox.expiration;
var suffix = debug ? ".dev." : ".prod.";
var manifest = [{"revision":"233692e6a59a401d25babb470b42d0ba","url":"assets/[name].11eba18b.js"},{"revision":"0db45f676295b8a5367f17939bfecde5","url":"assets/about.9f61fe0e.js"},{"revision":"701ce0f558c7c79468284db9441d9485","url":"assets/home.ae1a8a75.js"},{"revision":"b32b3c35ce2176e5239f0f5958d3ed08","url":"assets/index.d8d0085a.js"},{"revision":"a760355bef2fd5ded385ccabf84c8de0","url":"assets/index.f3bb671a.css"},{"revision":"cb39939b6c451569e4fa7e86b2413cd8","url":"index.html"},{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a7cdb86a9f313bf7d91abf2997c876f8","url":"workbox-v6.1.1/workbox-background-sync.dev.js"},{"revision":"c37bef1035f38894236763f70f847b01","url":"workbox-v6.1.1/workbox-background-sync.prod.js"},{"revision":"b4834c087eaf8da50fcce69a663cbcc0","url":"workbox-v6.1.1/workbox-broadcast-update.dev.js"},{"revision":"9224c426031572af4cbde2db64b7bba4","url":"workbox-v6.1.1/workbox-broadcast-update.prod.js"},{"revision":"bd4298725d2b05c1a7313861ca43119e","url":"workbox-v6.1.1/workbox-cacheable-response.dev.js"},{"revision":"0f99a971609d97b2e235d6f27347cce2","url":"workbox-v6.1.1/workbox-cacheable-response.prod.js"},{"revision":"0ab1809eb6d8f9823adf893131514fc3","url":"workbox-v6.1.1/workbox-core.dev.js"},{"revision":"85c2be1a0e73006ce9e9d1d0cc889459","url":"workbox-v6.1.1/workbox-core.prod.js"},{"revision":"9fb6484c2667d9df08928b0f2f702c76","url":"workbox-v6.1.1/workbox-expiration.dev.js"},{"revision":"c353b8b02a9452019c2ddd9a76620b10","url":"workbox-v6.1.1/workbox-expiration.prod.js"},{"revision":"6f64a834420ad2f28aa8af74c4673bfc","url":"workbox-v6.1.1/workbox-navigation-preload.dev.js"},{"revision":"b455c3e2414561b90c2dac557cf6e87a","url":"workbox-v6.1.1/workbox-navigation-preload.prod.js"},{"revision":"56973179ef1b3124a2f4cc1046b86b75","url":"workbox-v6.1.1/workbox-offline-ga.dev.js"},{"revision":"0b44aa740a7029014d1356317892b96a","url":"workbox-v6.1.1/workbox-offline-ga.prod.js"},{"revision":"6ad698a436eaee2c23637f36102cff02","url":"workbox-v6.1.1/workbox-precaching.dev.js"},{"revision":"b7b24145fd52a89d6127b3310efab41c","url":"workbox-v6.1.1/workbox-precaching.prod.js"},{"revision":"ee1d732eaf6f444875cdecd34df6ed6a","url":"workbox-v6.1.1/workbox-range-requests.dev.js"},{"revision":"591c63c72daadc424e86793e77b92560","url":"workbox-v6.1.1/workbox-range-requests.prod.js"},{"revision":"34f18376df91157ec6ea489e28f945ce","url":"workbox-v6.1.1/workbox-recipes.dev.js"},{"revision":"922bc61480b51855016fb3fc39304398","url":"workbox-v6.1.1/workbox-recipes.prod.js"},{"revision":"fce9cb70987cdba7f37660722ac9f61f","url":"workbox-v6.1.1/workbox-routing.dev.js"},{"revision":"ba807b7a301d7556f34ae12f94b6044e","url":"workbox-v6.1.1/workbox-routing.prod.js"},{"revision":"7385f58adf6fa4ea3ed0de08651d79c8","url":"workbox-v6.1.1/workbox-strategies.dev.js"},{"revision":"ae4e3f5028e8192585bdd0f3d0ef33e5","url":"workbox-v6.1.1/workbox-strategies.prod.js"},{"revision":"8ab7d86a8d8439a2a95614054205a42c","url":"workbox-v6.1.1/workbox-streams.dev.js"},{"revision":"6aca7378ad212a06ce9a64a55dea76ae","url":"workbox-v6.1.1/workbox-streams.prod.js"},{"revision":"55fb6379e95be0790836c1c942f00bd0","url":"workbox-v6.1.1/workbox-sw.js"},{"revision":"534033888a2eb22884af6c83e01c340e","url":"workbox-v6.1.1/workbox-window.dev.umd.js"},{"revision":"140fbae39a000992dca61016d87a6d8f","url":"workbox-v6.1.1/workbox-window.prod.umd.js"}].filter((entry) => {
return !entry.url || !entry.url.startsWith(useModulePathPrefix) || modules.some((module) => {
return entry.url.contains(suffix);
});
});
var cacheName = cacheNames.runtime;
var manifestURLs = [...manifest].map((entry) => {
const url = new URL(entry.url, self.location);
return url.href;
});
self.addEventListener("install", (event) => {
event.waitUntil(caches.open(cacheName).then((cache) => {
return cache.addAll(manifestURLs);
}));
});
self.addEventListener("activate", (event) => {
event.waitUntil(caches.delete(cacheNames.precache).then((result) => {
if (result)
console.log("Precached data removed");
else
console.log("No precache found");
}));
});
self.addEventListener("activate", (event) => {
event.waitUntil(caches.open(cacheName).then((cache) => {
cache.keys().then((keys) => {
keys.forEach((request) => {
if (!manifestURLs.includes(request.url))
cache.delete(request);
});
});
}));
});
registerRoute(({url}) => manifestURLs.includes(url.href), new NetworkFirst({cacheName}));
setDefaultHandler(new NetworkOnly());
setCatchHandler(async ({event}) => {
switch (event.request.destination) {
case "document":
return await caches.match("/index.html");
default:
return Promise.resolve(Response.error());
}
}); |
Awesome work!!!!! |
@userquin that's awesome 🙇 I'll try to test it in upcoming days |
Hello, I'm not well versed in PWA settings, and your plugin works really well as a 0-config tool (thanks for that).
However, I'm faced with a cache issue when I'm redeploying my app. In that case, I had to rename several api endpoints on my backend, but once the Vue app was built and deployed, all I got was 404 errors because I was still served the app from the cache. Since it's still in dev I simply manually unregistered the worker, but I what to do if that happens in production?
If I understand correctly, even if all my .js files and assets have a versioned hash in their name, as long as the index.html file is cached, it will still serve the old assets.
Is there a configuration I can setup (maybe it's still WIP on your side) to fix this, or if applicable, is there something you recommend? I found several solutions for this issue, but nothing looks really clean or fail-proof.
The text was updated successfully, but these errors were encountered: