SteGriff

Blog

Next & Previous

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 🛶