Busting SPA Chunk Cache for Active Users After a Deploy
How we used Pusher to reload the user’s browser without them noticing it
It’s a common problem with single page apps (SPA): when a deployment finishes successfully after the user has loaded the app, they still use the old and cached version until they refresh the website.
This problem aggravates with async chunks. The app is split in parts and each part is only loaded on demand, like when a user navigates to a respective section. The user might be in one part of the app while a deploy process succeeds, then if they navigate to another part, the chunks they request will have been removed from the server by the build script. Problem: this causes loading errors.
Pusher is the main ingredient to this solution.
On a successful deployment, the CI pushes an event to notify the frontend. There we catch and queue a page refresh. When the user starts browsing the website again, the first navigation is via a page redirect. We also guard pages where a refresh might cause loosing important page state.
This approach is technology-agnostic. At Hypefactors, we use Vue, but this solutions works equally well for React or other frameworks. And for the deploy event notification any push framework can be used. We went with Pusher because its easy to use and reliable. There is a free to try, and totally worth it. For deployments we use Netlify. It’s fast, easy to use with an integrated CDN, prerendering and so on.
Enough talk, lets see how its done.
First you need to register the needed app environments on Pusher. We chose to use separate pusher environments for each of our staging processes as opposed to using one for all environments. Inside each environment we created a public deployments channel.
Using one Pusher environment would require constant checking to make sure you don’t start refreshing the production app.
Setting up the Frontend
notify-deployment.js and paste the code bellow, filling in the noted places.
Now every time you want to notify the frontend that a deploy was successful you do
node path/to/notify-deployment.js production
PusherService.js file that you can import at the bootstrap of your app.
The tricky part here is to find a way to pass different pusher keys depending on the current environment (dev/staging/production). We import different config files based on the currently set
Then we pass the variables to the app via the Webpack DefinePlugin plugin.
Webpack will just replace
process.env.YOUR_VARIABLE with the same variable inside the
You can use this method to get access to other environment specific keys, urls and others you defined via
Now when your app loads, you need to subscribe to the development channel on Pusher. We use Vue so we do this in the App’s
Now this saves a
FORCE_RELOAD variable into localStorage via the
persist helper we have. This gives us an easy way to reload the user’s browser when they navigate around the app. We do this using Vue router’s
We check whether
FORCE_LOAD is saved in localStorage and whether it is true. We use our
load helper for that, which defaults to false if not set.
Next we check the route. If any of the parent matched routes is blacklisted, a.k.a. protected, it should not get refreshed, as precious form data might get lost. That means it will just wait for the next navigation event.
Finally we save
FORCE_RELOAD as false and we navigate to the location we wanted via a full page refresh.
Set up the Deployment Script
All we need to do now is, after a successful deployment, we run the
node notify-deployment --development
This will send an event to the
deployment channel that you want to silently reload.
We have helper commands in
package.json to make this easier to read and manage.
That’s all folks!
This approach of reloading is great for continuous deployment, especially when you deploy multiple times an hour.
The code and scripts have been collected in a gist for easy copy pasting. Check out https://gist.github.com/dobromir-hristov/50df8fbdf52de942ee70552646e2eeb7
p.s. did you know that Hypefactors is hiring?