We are developing a Vue.js application based on Vue CLI 3 with Vue Router and Webpack. The routes are lazy-loaded and the chunk file names contain a hash for cache busting. In general, everything is working fine.
However, there is a problem during the deployment. Steps to reproduce are the following.
- User opens the application (let's assume route "/"), thus the main chunk file is loaded.
- We change something in the application and deploy a new version.
- Old chunk files are removed
- New chunk files are being added (i.e. hashes in the chunk file names change)
- User clicks a link to another route (e.g. "/foo")
- An error occurs as the application tries to load a chunk file that has been renamed:
Error: "Loading CSS chunk foo failed. (/assets/css/foo.abc123.css)"
(this might be CSS or JavaScript)
- An error occurs as the application tries to load a chunk file that has been renamed:
What is the best way to avoid errors like this?
One approach that should work is just to retain old chunk files and delete them at a later time. That, however, plicates the deployment of new versions as you need to keep track of old versions and always also deploy the old chunk files with the new version.
Another (naive) approach is to just reload as soon as such an error is detected (e.g. Vue Lazy Routes & loading chunk failed). It somewhat works, but it reloads the old route, not the new one. But at least it ensure that consecutive route changes work again.
Any other ideas? Maybe there is something in webpack that could fix this?
We are developing a Vue.js application based on Vue CLI 3 with Vue Router and Webpack. The routes are lazy-loaded and the chunk file names contain a hash for cache busting. In general, everything is working fine.
However, there is a problem during the deployment. Steps to reproduce are the following.
- User opens the application (let's assume route "/"), thus the main chunk file is loaded.
- We change something in the application and deploy a new version.
- Old chunk files are removed
- New chunk files are being added (i.e. hashes in the chunk file names change)
- User clicks a link to another route (e.g. "/foo")
- An error occurs as the application tries to load a chunk file that has been renamed:
Error: "Loading CSS chunk foo failed. (/assets/css/foo.abc123.css)"
(this might be CSS or JavaScript)
- An error occurs as the application tries to load a chunk file that has been renamed:
What is the best way to avoid errors like this?
One approach that should work is just to retain old chunk files and delete them at a later time. That, however, plicates the deployment of new versions as you need to keep track of old versions and always also deploy the old chunk files with the new version.
Another (naive) approach is to just reload as soon as such an error is detected (e.g. Vue Lazy Routes & loading chunk failed). It somewhat works, but it reloads the old route, not the new one. But at least it ensure that consecutive route changes work again.
Any other ideas? Maybe there is something in webpack that could fix this?
Share Improve this question asked Feb 26, 2019 at 16:10 strstr 45k18 gold badges114 silver badges134 bronze badges 5- Isnt hot reload (webpack) supposed to do exactly what you are asking? – bigless Commented Feb 26, 2019 at 16:25
- @bigless During development yes, but not in production. – str Commented Feb 26, 2019 at 17:20
- Dynamic hash links aka chunks on production is not good idea. It should be in form of query params or anchors but not links that throw 404.. – bigless Commented Feb 26, 2019 at 18:10
- @bigless Hashes in the file names obviously have their problems, but as far as I can tell it is the industry-standard (e.g. Google remends it). Query parameters did not work well years ago (e.g. because of proxies) but they might work today. I cannot find a definite resource on that. But anchors most definitely won't work. – str Commented Feb 26, 2019 at 19:11
- we use query versioning on production for years and it works. we generate version(f.e. ?v=1.66.5) into template on package build and include it like suffix for every local resource on page. – bigless Commented Feb 26, 2019 at 20:01
2 Answers
Reset to default 3DoNOT cache the entry file(usually index.html). We add:
expires 0;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate';
in our nginx server config.
Then, after you refreshed the client's code, you can use the vue-router's error hook to detect the error and do something properly.
As long as you have a versioned API, you can use the old app files (just leave them on the server and delete after a vew days).
You will get problems as soon as your API changes during deployments.
I assume, you deploy a new API each time you deploy new JS code.
Then you can:
- Pass on the API version (simply use the git hash) to the application as header with every response (JS resources, CSS, API requests, 404 responses)
- Store the API version in your main JS entry point (or make it accessible somehow, e.g. as generated constant)
- On each server response, check if the Server version matches your main client version.
- If it does not: Display a prominent warning to the user (like the cookie banners) that he should reload the page (=> allows the user to save chnages in hope the API did not change for that save button).
For async ponents, we display normal 'not found' messages if loading fails, together with a reload button that appears instead of the ponent. Reloading without user interaction will cause a lot of confusion.