Adding cache-busting chunk-hashes to Vue CLI output JS
Cache-busting chunk-hashes. Chunk-busting cache-hashes. It’s almost onomatopoeic; reminiscent of a physical break-in at Mt. Gox.
But no, today, we’re talking about those squirly bits that change your JS output files from
0.js
1.js
app.js
vendors.js
to
0.4a0685f0.bundle.js
1.e36287a7.bundle.js
app.b6eadd11.bundle.js
chunk-vendors.111bdf30.bundle.js
Now why on Earth would you want to do that
Because caching!
Our UAT and Live sites sit behind Cloudflare (I am not my employer – mixed feelings… don’t judge) which does some pretty eager caching of anything that’s not dynamic, including JS assets.
When we pushed a new version of our JS frontend app to UAT, the browsers couldn’t pick up the files, because when they requested them from CF it would proudly announce “cache hit! 🤩 i got it! 🤑 yeah baby 🔜🔜” and serve up the old JS files.
The situation
We are building this frontend in Vue.js 2.x with Vue-cli (we’ll move to Vite [and maybe Vue 3] when we can…) which is Webpack behind the scenes.
Now, I thought this chunk-hashing was a default thing in Webpack but it wasn’t happening for us. So I added some config:
The config change
In vue.config.js
we had this:
module.exports = {
transpileDependencies: ["vuetify"],
css: {
loaderOptions: {
scss: {
additionalData: `@import "~@/scss/main.scss";`,
},
},
extract: {
ignoreOrder: true,
},
},
chainWebpack: (config) => {
config.module.rule("eslint").use("eslint-loader").options({
fix: true,
});
},
};
We added a little configureWebpack
clause just before chainWebpack
:
configureWebpack: {
output:
process.env.NODE_ENV === "development"
? {}
: {
filename: "js/[name].[chunkhash:8].bundle.js",
chunkFilename: "js/[name].[chunkhash:8].bundle.js",
},
},
Now, you can play around with that. You probably don’t need the .bundle
part; honestly I’m not sure why I kept that in.
Why does it have the ternary based on development
environment?
Aha! Well, I learned by doing, that without this, you will break npm run serve
. If the files are output with the chunkhash in combination with watch/serve, you’ll get an error like:
error in css/app.69fd9761.css
Cannot use [chunkhash] or [contenthash] for chunk in 'js/[name].[chunkhash:8].bundle.js' (use [hash] instead)
So, yeah. Best to make it conditional. And you have to pass {}
, not null
, as null
will blow away all the existing default config that you can’t see.
Conclusion
In your index.html
, webpack will pick up that fact that the underlying script filenames have changed, and will inject the new filenames into the output html on build.
So there you have it. A little snippet to defeat Cloudflare (and other providers) caches when they thwart your shiny new deployment.
Commit early, commit often 🛶