preface
This article mainly discusses the use of strong cache and negotiated cache practice, without expanding the basic concepts. I’ve written about this before, but if you’re interested, check out my blog: Web caching.
Mainly from nodeJS practice strong cache and negotiation cache and webpack project update strategy.
NodeJs practices strong cache negotiation caching
It’s actually very easy to distinguish between strong cache and negotiated cache, and where to use it. Usually when we visit a website, we first go to an HTML file, and the JS, CSS, images, audio and so on referenced in the HTML file are given to us to look at a website.
So does HTML use strong cache lines?
I don’t even have to think about it, no, why, let’s say I’m using strong caching in HTML, and when I need to change a style somewhere, AND I change it and I go back online CSS files, HTML files (because CSS has changed, so HTML has to reintroduce CSS files, so HTML has to change and go back online), But HTML uses a strong cache, and during the cache the browser reads the HTML file directly from the strong cache, so that you go back online but it’s the same HTML, it’s the same content, and you have to force the user to refresh it so that there’s new content, does that make sense? It doesn’t make sense.
So HTML should use a negotiated cache, while CSS, JS, etc. should use a strong cache.
Examples refer to cache-Control-nodejs-demo
Code parsing
The code uses a server built by NodeJS, and sets the cache by setting header, and uses two NPM packages, etag and Fresh, to calculate the eTAG value of the negotiated cache and determine whether it has expired.
Here is the index.html file, which references JS and CSS
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>node demo</title>
<link rel="stylesheet" href="/index.css">
</head>
<body>
<h1>hello world</h1>
<script src="/index.js"></script>
</body>
Copy the code
index.css
h1 {
color: skyblue;
}
Copy the code
js
console.log('node cache demo')
Copy the code
Here is the nodejs service code: first check if the URL goes to the root directory, then return the index.html file path
const pathname = url.parse(req.url, true).pathname
// Obtain the absolute file path according to the requested path
if (pathname === '/') {
filePath = path.join(__dirname, '/index.html')
isHtml = true
} else {
filePath = path.join(__dirname, 'static', pathname)
isHtml = false
}
Copy the code
If not, 404 will be returned. If the file exists, then the HTML negotiation cache will be set, that is, the last-Modified and ETag response headers, where ETag () is the ETag package method. Fresh () is used to determine whether the negotiation cache has expired. This article covers the source code for both methods, which you can read if you are interested: Front-end Caching Best Practices
fs.stat(filePath, function (err, stat) {
if (err) {
res.writeHead(404.'not found')
res.end('<h1>404 Not Found</h1>')}else {
if (isHtml) {
// HTML files use negotiation cache
const lastModified = stat.mtime.toUTCString()
const fileEtag = etag(stat)
res.setHeader('Cache-Control'.'public, max-age=0')
res.setHeader('Last-Modified', lastModified)
res.setHeader('ETag', fileEtag)
// Determine whether the cache is up to date based on the request header
isFresh = fresh(req.headers, {
etag: fileEtag,
'last-modified': lastModified
})
} else {
// Other static resources use strong caching
res.setHeader('Cache-Control'.'public, max-age=3600')
}
fs.readFile(filePath, 'utf-8'.function (err, fileContent) {
if (err) {
res.writeHead(404.'not found')
res.end('<h1>404 Not Found</h1>')}else {
if (isHtml && isFresh) {
// If the cache is up to date, 304 status code is returned
// 304 will not appear because other resources use strong caching
res.writeHead(304.'Not Modified')}else {
res.write(fileContent, 'utf-8')
}
res.end()
}
})
}
})
Copy the code
Effect: you can see below the first entry is 200 status code
Refresh to see that the cache Settings are in effect, and the response headers are set as follows:
Cache update issues
Now comes a sudden requirement to change the font color of H1 to red. I will index. The CSS changes, but didn’t see want to see when visit the web site, the effect of font or blue, because in addition to the HTML file, the rest of the resource usage is strong cache, so every visit is to visit the inside of the cache resources (strong cache expiration period), had to force refresh to skip the cache request again to get the latest file.
This is obviously not reasonable, so how to do it?
The HTML file will be asked if it is up to date every time it is accessed, so just change the CSS in the HTML file to introduce the address
add a version number, because every time the HTML sends a request asking if it is out of date, this will do the trick.
This works, but you can’t just use one CSS file for development. It’s often multiple. Every time I change one CSS file, the rest of the CSS is updated but the rest of the CSS is not changed at all.
For more complex, you can use the summary information of the file to rename the resource file to achieve a precise cache control. For more details, you can read this article: front-end static resource cache and update
Webpack practice
The WebPack construction project can use the summary information of the file to rename the resource file to achieve a precise cache control, below do a small Demo to learn. The source address
Here is a simple setup for the React project:
├─ Public │ ├─ ├─ ├─ ├.css │ ├─ ├.js │ ├─ ├─ ├─ ├.js │ ├─ ├── ├.js │ ├─ ├── ├.js │ ├── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ─ App. JSX └ ─ ─ webpack. Config. JsCopy the code
Focus on the webpack.config.js configuration
Package output configuration: a hash value is inserted after it for renaming; Then the CSS is also split and renamed using hash for precise cache control.
output: {
path: path.join(__dirname, './dist'),
filename: 'bundle.[chunkhash].js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'webpack cache demo'.template: './public/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[hash].css'})].Copy the code
Here I upload to the server to see the effect, the first time is a new request is ok, the second access is also HTML negotiation cache, CSS, JS and other resources strong cache.
The previous app.css file has not been modified
body {
background-color: #f5f5f5;
}
h1 {
color: skyblue;
}
p {
color: pink;
}
Copy the code
After the modification of app. CSS file, the color of H1 and P are exchanged
body {
background-color: #f5f5f5;
}
h1 {
color: pink;
}
p {
color: skyblue;
}
Copy the code
The packaged CSS file is shown here, and the HTML file has also changed the reference address
In this way, resource files can be renamed to achieve a precise cache control.
A brief description of [chunkhash] in configuration
Webpack provides three hash methods: Hash, Chunkhash, and Contenthash.
The [hash] substitution can be used to include a build-specific hash in the filename, but it is better to use the [chunkhash] substitution to include a chunk-specific hash in the filename. The contenthash substitution string creates a unique hash based on the resource content. When the content of the resource changes, the [Contenthash] changes.
As you can see from the webpack documentation, the hash is project-specific, and whenever something changes in the project, the overall hash changes.
Chunkhash depends on the import file to parse the file, build the corresponding chunk, and generate the corresponding hash value. Therefore, the CSS content is changed, and js references the CSS will also change the hash value.
Contenthash generates the hash based on the contents of the file. Changes in the content will change the hash, and other references will not change the hash.
Refer to the article
Front-end static resource caching and updating
Front-end caching best practices
Webpack cache