If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeLet's talk about caching.
Imagine someone comes to our site and downloads layout.js
. In a typical setup, when they visit the next page, their browser makes a request back to our server for layout.js
. If that file has not changed, our server sends back an empty response that says:
Hey! Use your cached version dude!
But if the file has changed, our server sends back the new, updated layout.js
.
This caching strategy is called "validation". On the bright side, when layout.js
changes, browsers will automatically go get the new, updated file. With validation caching - which is what most web servers do by default - you shouldn't have any problems where your user needs to "clear their cache" or "force refresh" to see the updated CSS or JS.
But... validation caching is not the fastest way to cache! Nope, you can instead configure your web server to use expiration caching. In this situation, when your user downloads layout.js
, the server will say "Hey browser! Cache this for 1 year!". Then, as the user browses your site, they instantly use the cached file... instead of making a request to our server to ask if it's still valid.
Yep, expiration caching rocks! So why don't we all use it? Well, what if we update layout.js
? With expiration caching... well... our existing users would never download the new file. Their browsers would use the old file for a whole year!
To get expiration caching to work, whenever layout.js
changes, the URL to it needs to change... either by adding a query parameter, like /build/layout.js?v=5
or by changing the filename.
Webpack has a great way to do that automatically.
At the top of webpack.config.js
, add a new variable called useVersioning
set to true
:
... lines 1 - 7 | |
const useVersioning = true; | |
... lines 9 - 165 |
This will let us disable versioning easily if we want to.
Start under output
: the filename
option controls the JavaScript files. Change this: if useVersioning
, then change the filename to be [name].[hash:6].js
:
... lines 1 - 7 | |
const useVersioning = true; | |
... lines 9 - 38 | |
const webpackConfig = { | |
... lines 40 - 44 | |
output: { | |
... line 46 | |
filename: useVersioning ? '[name].[hash:6].js' : '[name].js', | |
... line 48 | |
}, | |
... lines 50 - 140 | |
}; | |
... lines 142 - 165 |
[hash]
is another special wildcard: it's a hash based on the file's contents. And this outputs just the first 6 characters.
To control the CSS filenames, find the plugins
section. For ExtractTextPlugin
, add useVersioning
. If it's on, use [name].[contenthash:6].css
:
... lines 1 - 6 | |
const useVersioning = true; | |
... lines 8 - 37 | |
const webpackConfig = { | |
... lines 39 - 105 | |
plugins: [ | |
... lines 107 - 128 | |
new ExtractTextPlugin( | |
useVersioning ? '[name].[contenthash:6].css' : '[name].css' | |
), | |
], | |
... lines 133 - 137 | |
}; | |
... lines 139 - 162 |
Tip
If you're using Webpack 4 or higher, we recommend using the mini-css-extract-plugin
instead of ExtractTextPlugin. However, if you do use ExtractTextPlugin,
use [name].[md5:contenthash:hex:6]
to avoid an issue with changes in Webpack 4.
ExtractTextPlugin exposes a [contenthash]
wildcard.
And... that's it! Find your webpack terminal. First, clear the build directory:
rm -rf web/build/*
Now run:
yarn dev
So cool! By changing two lines, all of the filenames contain a hash! Now, whenever we change a file, its built filename changes.
Of course... well... our site is totally broken! Whoops! We created a new problem: our app needs to point to the new filenames... but those hashes will change constantly!
Let's fix that next!
// composer.json
{
"require": {
"php": "^7.2.0",
"symfony/symfony": "3.3.*", // v3.3.16
"twig/twig": "2.10.*", // v2.10.0
"doctrine/orm": "^2.5", // v2.7.0
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
"symfony/swiftmailer-bundle": "^2.3", // v2.6.3
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.4.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"friendsofsymfony/user-bundle": "^2.0", // v2.1.2
"doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
"doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.6
"symfony/phpunit-bridge": "^3.0" // v3.3.5
}
}
// package.json
{
"dependencies": [],
"devDependencies": {
"babel-core": "^6.25.0", // 6.25.0
"babel-loader": "^7.1.1", // 7.1.1
"babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
"babel-preset-env": "^1.6.0", // 1.6.0
"bootstrap-sass": "^3.3.7", // 3.3.7
"clean-webpack-plugin": "^0.1.16", // 0.1.16
"copy-webpack-plugin": "^4.0.1", // 4.0.1
"core-js": "^2.4.1", // 2.4.1
"css-loader": "^0.28.4", // 0.28.4
"extract-text-webpack-plugin": "^3.0.0", // 3.0.0
"file-loader": "^0.11.2", // 0.11.2
"font-awesome": "^4.7.0", // 4.7.0
"jquery": "^3.2.1", // 3.2.1
"lodash": "^4.17.4", // 4.17.4
"node-sass": "^4.5.3", // 4.5.3
"resolve-url-loader": "^2.1.0", // 2.1.0
"sass-loader": "^6.0.6", // 6.0.6
"style-loader": "^0.18.2", // 0.18.2
"sweetalert2": "^6.6.6", // 6.6.6
"webpack": "^3.4.1", // 3.4.1
"webpack-chunk-hash": "^0.4.0", // 0.4.0
"webpack-dev-server": "^2.6.1", // 2.6.1
"webpack-manifest-plugin": "^1.2.1" // 1.2.1
}
}
With the WP4 compatible version contenthash is changed to just hash.