Preact-cli: How to unregister / remove any registered service workers?

Created on 17 Sep 2017  路  36Comments  路  Source: preactjs/preact-cli

Is it possible to remove any registered service workers by this CLI for any visitors on a production app? The application runs currently with the precache sw.js and I would like to have it removed. I understand that I can do it myself with the console tools, but I can't let my users do the same.

Most helpful comment

Yeah, I documented myself a bit more on the subject.
Here is a good read for other PWA newbie like me. It clearly defines updates strategies for service-workers here, and here. Also, I have yet to read about push and sync which may solve some stuff I guess.

Anyway, thanks again @prateekbh you made me realize my mistake. :)

All 36 comments

sadly this line prevent you from doing so
https://github.com/developit/preact-cli/blob/19067c7091b463b2a70b319e87d15037880b4fbc/src/lib/entry.js#L7

although I guess I can add a quick solution to this, will try to add a flag.
@reznord u think a flag `--no-pwa should be a good solution here? if yes, you'd need to add in you interactive stuff as well

a flag `--no-pwa should be a good solution here
This defies the whole purpose of preact-cli.

But still we can unregister a service worker which makes sense (I still have to try it out).

This defies the whole purpose of preact-cli.

It shouldn't right? not sure if everything needs to be a Offline ready or have SW behind it. e.g. a documentation of a project. or for what ever reason @robinvdvleuten wants it.

preact-cli is the cli for building preact web apps. PWA preferred but not necessary

I use it indeed mainly to prevent myself from spending a couple of hours on a decent webpack setup. The preact-cli project gets me starting quickly on any prototype I would like to make. It would be very nice if it's possible to opt-out yourself from the PWA when generating a new project, but it would also be very nice to be able to opt-out in any later stages of your project.

The CRA has a similar setup. But it feels like I have more control over the service-worker registration process. The preact-cli just generates it and I am never able to unregister it again.

opt-out/opt-in can be controlled by a build step flag at anytime

makes sense to me.

@developit @lukeed what do you think about this?

@developit @lukeed @reznord lemme know if the approach makes sense, m up to send this PR

Should we use a more specific flag like --no-sw ?

That makes sense! This should be only for the build step right?

Same idea, but what about --no-offline?

well --no-offline can be achieved using preact-cli-sw-precache-plugin by giving empty array for precache, the problem is for not being able to add sw.js

It would also be nice if you're able to opt-out of the sw.js if your app is already running on production

@robinvdvleuten to opt-out of already running sw.js you'll need to add some script to add in your code. pretty trivial

@prateekbh do you maybe have a small example?

Hey !
I don't know if I step in the wrong thread but, what if instead of unregistering the worker we could provide an option to update our worker ?
According to this cool reliable post :

Update your service worker JavaScript file. When the user navigates to your site, the browser tries to redownload the script file that defined the service worker in the background. If there is even a byte's difference in the service worker file compared to what it currently has, it considers it new.

What do I mean by this ?

We could keep the best of both worlds: having service workers by default + invalidate any worker at any time.

I am really new to this, but would it feasible to have an override sw.config.js file, where we could set at least :

  1. A specific cacheName. Today preact-cli ships cacheName = "sw-precache-v3-sw-precache-webpack-plugin-" + (self.registration ? self.registration.scope : "") by default. I understand that if we could just change the name of this cacheName variable we could force our worker to be invalidated (so files would be updated directly from the net, not from the worker, then cached again until new renaming)
  2. Choose files to be cached. Today preact-cli forces cache of every static assets (right??). Being able to exclude some folders/files could be a good starting point. That way, some developers could as well kind of "opt-out" by excluding specific folders.

Regarding the subject behind the philosophy of preact-cli: what personally brings me to preact in the first place was : 4.5kb + 100/100 lighthouse which resonated with me. The former assertion is incredibly powerful I think, and to keep it service-workers should be a force, not a weakness.

I don't know if you consider my literature completely out of subject, but I guess this approach could also allow developers to have fine control over which resources they would like to have served offline or not... as well as unregister/update workers.

EDIT: I think service workers are the new big guns of the web, and I don't really see a reason not to use them when available in the hosting browser. Even for most trivial apps/sites, workers can lead to significantly higher UX (serving offline, instant page-load) and drastic diminution of requests (serving from service-worker's cache directly). In a "ancient-times" website, I don't see why one would prevent vendors javascripts/css from being stored in the browser. That being said, I think we should encourage good habits in teaching how PWA can be simple to handle USING PREACT-CLI, while allowing some flexibility. Allowing --no-sw would literally mean "sometimes, service-workers are a bad practice so we let you not use it at all" or "if you don't know how to handle PWA, you can disable it". I would prefer something like "hey, PWA are great. Let us help you make it easier."

EDIT2: instead of --no-sw I would suggest something like --new-sw that would basically create a new unique "id" for the cacheName (based on timestamp or whatever) thus invalidating any previous workers.

To support my request as well as replying to the initial question, I would suggest read of this specific SO answer to ensure the sw.js file is correctly fetched from the network (and not from the cache). Then, I would specifically update my workers cacheName with some new stamp on it. This should correcly unregister previous workers.

@Jonarod Thanks for the detailed explanation. I totally buy your point.
However all what you said is already totally possible with preact-cli-sw-precache-plugin. Whit this you can control every fine detail for your service worker.

But I am of the opinion that, lets not be rigid about anything. Currently sw-precache does not give you control for the custom login in the produced service worker. Also there might be other reasons where the dev wants to do it totally differently, hence the flag to disable our auto registration process.

instead of --no-sw I would suggest something like --new-sw that would basically create a new unique "id" for the cacheName

This automatically happens if you change anything in your app(which is a part precache) or any detail in preact-cli-sw-precache-plugin, hence you don't really need to modify the cacheName but just the application code which you need to update.

TLDR: If you want to modify/have fine grained control on the service worker use preact-cli-sw-precache-plugin

For any reason where you dont buy our sw approach ad want to do it on your own --service-worker false is your friend.

@prateekbh Thanks for the gist! Excuse my inexperience with serviceworker registrations though. I can see myself adding this gist snippet to my Preact app code, so that the serviceworker unregisters. But is it then also possible to remove the sw.js file in the same deployment? When I do something similar, the current sw.js stays registered in my browser and the updated app js is never loaded by the client. I try to get my head around the serviceworker api, thanks for all the help!

@prateekbh Sorry for my ignorance on the matter, but how would you force new cacheName string using preact-cli-sw-precache-plugin ?
Forcing a new name into preact.config.js would defeat the purpose:
If I force the cacheName to be a fixed string of my choice: I will have to manually maintain it.
If I force the cacheName to be timestamp-like pattern: I will force my users to re-update their cache at each build I do...

Correct me if I am wrong, but using preact-cli-sw-precache-plugin, I don't think it is feasible to manually "stage" the worker's name as I wish ? Hence my suggestion to generate a "new worker" using --new-sw at build time from preact-cli. This command would basically mark the worker's cacheName ONLY when I need to, thus correctly unregistering the one currently served to my users.

The original question by @robinvdvleuten resonated with me because I understood he wanted to actually UPDATE his worker in production and not to totally remove PWA from his app (which is already live). Also, it happens that I have this same frustration because after each deploy I have to go Ctrl+Shift+I > Right-click the reload button > "Empty Cache and Hard reload" which, as @robinvdvleuten states

I understand that I can do it myself with the console tools, but I can't let my users do the same.

Obviously one option is to totally stop thinking about PWA and step aside because it is just frustrating, but after seeing this thread closed with this comment, I thought I'd suggest something else.

In my case, (and maybe @robinvdvleuten one ?), --new-sw would be a proper answer to "invalidate" workers in the pure sense of it. However, I agree my request is not the correct answer to not using workers on my "next Preact PWA" though ahah :P

Anyway, thanks for pointing me to the plugin, I had no clue of it to be honest. I will try to play around with it and see if I can tweak some stuff. :)

@robinvdvleuten so here's how it should work for you for now.
a.) You add that gist to your code(plz test on local if it actually unregisters your sw) bundle route
b.) deploy this code(using the new flag)
c.) your users will see a new hash on your bundle and precache that. This time since the service worker was already there, nothing will happen.
d.) on second visit new bundle code will be executed and the service worker will be unregistered. Also no new sw will registered.

@Jonarod @robinvdvleuten ok if you want to still have the service worker and totally remove any already precached version here is what u write in your preact.config.js

const preactCliSwPrecachePlugin = require('preact-cli-sw-precache');

export default function (config) {
  const precacheConfig = {
    staticFileGlobs: [], //empty array so nothing will be a precached at all
    clientsClaim: true, // this sw will now control all tabs of your site open
    skipWaiting: true, // as soon as your browsers looks at this it unregister already registered sw.js and makes this one main
  };

  return preactCliSwPrecachePlugin(config, precacheConfig);
}

This will strip the registered sw.js immedeiately and make this new sw.js your service worker. Although be informed that now sw will have no role to play and will just be a perf overhead. So if this is all what u want, better unregister it.

also @Jonarod the way current things works is when u change a code in bundle or remove/add anything in precache entries or introduce/remove any runtime caching strategy. there is some or other change in the final sw.js this makes the browser unregister the older one and install the fresh one.

So,
A.) you do not need to change something cache name, virtually any change in sw.js will reinstall the fresh name
B.) If you have A.js, B.js, C.js. and you change code in B.js precache will add a new B.js with the new hash and not should not re-download A.js/C.js as far as i know.

Do let me know if you're trying to achieve something else with changing the cache name, would be happy to help

@prateekbh thanks again for you time !
For my part, this is not quite what I need.
I want service-workers for my app. I think it really improves everything.
However, whenever I make a new change to my app, I would like to control replacement of my sw.js because I need my users to have the new site-version.

Actually, my problem is quite straightforward, you will get it with this example: if you were to change preactjs.com main color from purple to orange. I guess you would have to tweak some styles and then build > deploy the site again.
However, I understand that with current default options of preact-cli I will see the purple color of preactjs.com forever, unless I personally go and clear my cache.
So basically, how do you handle that ?
With my poor knowledge of preact and web-dev so far, I am unfortunately NOT able to broadcast changes to my users as-is: whenever I deploy, my users keep the same deprecated version of my site indefinitely as they have no clue they should refresh the page AND clear their cache (which is unfeasible on a UX point of view). For the little story, I first blamed Netlify to not invalidate my cache over their CDN before undertanding what service-workers were for AHAHA (I am learning !).
So my question is: what is the best way to PUSH changes to my users according to you @prateekbh ?

Well, I might do something wrong then.
In my side, if I make changes to A.js, then deploy to Netlify, I don't get changes unless I clear my cache. Even with new hashed version of my assets, it seems like my sw.js is always serving my cache.
:/

hmmm intereseting.. can i get the URL ur webapp?
would love to check.. is ur sw.js itself having cache headers?

I see you said this:

c.) your users will see a new hash on your bundle and precache that. This time since the service worker was already there, nothing will happen.
d.) on second visit new bundle code will be executed and the service worker will be unregistered. Also no new sw will registered.

Now I am wondering if I actually refreshed the app twice before complaining about the sw.js.
Give me 1mn to deploy a basic change and refresh it twice to see what happens...

AHAH ok. My bad.
Refreshing the app TWICE takes the changes...
So basically, the reproduce steps are:

  1. git add . && git commit && git push origin master
  2. Github sends commit webhook to Netlify
  3. Netlify rebuilds the app and serves it through their CDN
  4. I go to my url and hit refresh => nothing changes (this is where I was stuck initially)
  5. I go hit refresh (again) => changes apply

That is a bit strange to me but I understand it has nothing to do with preact-cli rather it is a normal behavior of service-workers needing 2 passes to be unregistered...
Is there any "hack" to better UX like forcing the first sw.js to die at first refresh ??

@prateekbh here's my cache header for the sw.js cache-control:public, max-age=0, must-revalidate

@Jonarod what u said above is expected. Here's why: To make your app fast you added sw, which returns index.html/js/css from cache and thus your app is ultra fast. 馃憤

now you deploy another version, and person X comes to your app. Due to already registered sw your app opens quick and meanwhile your browser finds out and download and re-register the new one. And this is exactly what is happening to you.

Now if you need something like a force switch, here an approach you can take.
In your client js, have a post message handler. This handler if receives msg X will show a bottom bar, tellin user that "hey new updates, plz refresh to get them". or it receives msg Y it auto refreshes the window.

now from next deployment you can add a small script to ur sw.js in which on "install" event you can send these messages, if super imp deployment happens send Y/X from your sw.js

Yeah, I documented myself a bit more on the subject.
Here is a good read for other PWA newbie like me. It clearly defines updates strategies for service-workers here, and here. Also, I have yet to read about push and sync which may solve some stuff I guess.

Anyway, thanks again @prateekbh you made me realize my mistake. :)

happy to help 馃槂, hit me up on any individual channel if u need further help.

We're also facing the same issues with serviceworkers (caching), and are thinking that in the short term just disabling them might help, as we're rolling out frequent updates. ServiceWorkers are a killer feature, and I love them, but the double refresh does hurt - but the idea of showing "New updates" would solve this as well.

Good initiative, and would be happy to help test your PR on our site.

@joshuataylor adding code to show a the UI of new updates available might not be best suited for core preact-cli but would be best to have a separate component or a plugin maybe..

The opened PR will just help you in disabling the service workers.

Yeah, I meant as a separate addon, obviously not in core :).

use this, while keeping the original service worker and change some bits in the SW js file so it updates and unregisters itself.

     if ('serviceWorker' in navigator) {
            navigator.serviceWorker.getRegistrations()
                .then(function(registrations) {
                    for(let registration of registrations) {
                        registration.unregister();
                    }
                });
        }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

hardcoar picture hardcoar  路  3Comments

ethanwu10 picture ethanwu10  路  3Comments

andybons picture andybons  路  3Comments

c0debreaker picture c0debreaker  路  4Comments

scottmas picture scottmas  路  3Comments