Preface
Hello everyone , I'm Lin Sanxin , Speak the most difficult knowledge points in the most easy to understand words It's my motto , Foundation is the premise of advanced It's my first heart
background
Whether in development or interview ,HTTP cache It's all very important , This is reflected in two aspects :
- In development : Reasonable use
HTTP cache
It can improve the performance of front-end pages - During the interview :
HTTP cache
It's a high-frequency question in the interview
So this article , I don't talk nonsense , I'll go through Nodejs
The simple practice of , The most easy to understand HTTP cache , Through this article, you will be able to understand and master it !!!
Lead to
Get ready
Create folder
cache-study
, And prepare the environmentnpm init
install
Koa、nodemon
npm i koa -D npm i nodemon -g
- establish
index.js、index.html、static Folder
index.html
<!DOCTYPE html> <html lang="en"> <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>Document</title> <link rel="stylesheet" href="./static/css/index.css"> </head> <body> <div class="box"> </div> </body> </html>
static/css/index.css
.box { width: 500px; height: 300px; background-image: url('../image/guang.jpg'); background-size: 100% 100%; color: #000; }
static/image/guang.jpg
index.js
const Koa = require('koa') const fs = require('fs') const path = require('path') const mimes = { css: 'text/css', less: 'text/css', gif: 'image/gif', html: 'text/html', ico: 'image/x-icon', jpeg: 'image/jpeg', jpg: 'image/jpeg', js: 'text/javascript', json: 'application/json', pdf: 'application/pdf', png: 'image/png', svg: 'image/svg+xml', swf: 'application/x-shockwave-flash', tiff: 'image/tiff', txt: 'text/plain', wav: 'audio/x-wav', wma: 'audio/x-ms-wma', wmv: 'video/x-ms-wmv', xml: 'text/xml', } // Get the type of file function parseMime(url) { // path.extname Get the suffix of the file in the path let extName = path.extname(url) extName = extName ? extName.slice(1) : 'unknown' return mimes[extName] } // Convert the file to the format required for transmission const parseStatic = (dir) => { return new Promise((resolve) => { resolve(fs.readFileSync(dir), 'binary') }) } const app = new Koa() app.use(async (ctx) => { const url = ctx.request.url if (url === '/') { // Access the root path and return index.html ctx.set('Content-Type', 'text/html') ctx.body = await parseStatic('./index.html') } else { const filePath = path.resolve(__dirname, `.${url}`) // Set type ctx.set('Content-Type', parseMime(url)) // Set transmission ctx.body = await parseStatic(filePath) } }) app.listen(9898, () => { console.log('start at port 9898') })
Launch page
Now you can enter... In the terminal
nodemon index
, See the display below , It means that the service has been started successfully
At this point, you can enter... In the browser link http://localhost:9898/
, Open and see the following page , It means that the page is accessed successfully !!!
HTTP Cache type
HTTP cache
There are two common types of :
Strong cache
: It can be determined by one of these two fieldsexpires
cache-control( Higher priority )
Negotiate the cache
: It can be determined by one of these two pairs of fieldsLast-Modified,If-Modified-Since
Etag,If-None-Match( Higher priority )
Strong cache
Let's start with Strong cache
expires
We just need to set the response header expires
At current time + 30s
That's it
app.use(async (ctx) => {
const url = ctx.request.url
if (url === '/') {
// Access the root path and return index.html
ctx.set('Content-Type', 'text/html')
ctx.body = await parseStatic('./index.html')
} else {
const filePath = path.resolve(__dirname, `.${url}`)
// Set type
ctx.set('Content-Type', parseMime(url))
// Set up Expires Response head
const time = new Date(Date.now() + 30000).toUTCString()
ctx.set('Expires', time)
// Set transmission
ctx.body = await parseStatic(filePath)
}
})
Then we refresh the front page , We can see that there is one more in the response header of the requested resource expires
Field of
also , stay 30s Inside , After we refresh , See the request is to go memory
, It means , adopt expires
The time effect of setting strong cache is 30s, this 30s within , Resources will go to the local cache , Instead of re requesting
Be careful : Sometimes you Nodejs Code update aging time , However, it is found that the front-end page is still on the time limit of the code , This is the time , You can put this Disabled cache
Hook , Then refresh , Cancel... Tick again
cache-control
Actually cache-control
Follow expires
The effect is almost the same , It's just that the values of these two fields are different , The former is set to Number of seconds
, The latter is set to Number of milliseconds
app.use(async (ctx) => {
const url = ctx.request.url
if (url === '/') {
// Access the root path and return index.html
ctx.set('Content-Type', 'text/html')
ctx.body = await parseStatic('./index.html')
} else {
const filePath = path.resolve(__dirname, `.${url}`)
// Set type
ctx.set('Content-Type', parseMime(url))
// Set up Cache-Control Response head
ctx.set('Cache-Control', 'max-age=30')
// Set transmission
ctx.body = await parseStatic(filePath)
}
})
There are too many response headers on the front-end page cache-control
This field , And 30s Go to local cache in the , I won't ask the server
Negotiate the cache
And Strong cache
The difference is , Strong cache
Is within the time limit , Don't go to the server , Only local cache ; and Negotiate the cache
It's the server side , If you request a resource , When you go to the server , Find out Hit cache
Then return to 304
, Otherwise, the requested resource is returned , What's that Hit cache
Well ? Let's talk about that
Last-Modified,If-Modified-Since
In a nutshell :
- The first time a resource is requested , The server will send the requested resources to
Last modification time
As in the response headerLast-Modified
Send the value to the browser and save it in the browser - The second time a resource is requested , The browser will treat the time just stored as the request header
If-Modified-Since
Value , To the server , The server obtains this time and compares it with the last modification time of the requested resource - If the two times are the same , It means that this resource has not been modified , That's it
Hit cache
, Then return304
, If it's not the same , It indicates that this resource has been modified , beMiss cache
, The modified new resource is returned
// Get file information
const getFileStat = (path) => {
return new Promise((resolve) => {
fs.stat(path, (_, stat) => {
resolve(stat)
})
})
}
app.use(async (ctx) => {
const url = ctx.request.url
if (url === '/') {
// Access the root path and return index.html
ctx.set('Content-Type', 'text/html')
ctx.body = await parseStatic('./index.html')
} else {
const filePath = path.resolve(__dirname, `.${url}`)
const ifModifiedSince = ctx.request.header['if-modified-since']
const fileStat = await getFileStat(filePath)
console.log(new Date(fileStat.mtime).getTime())
ctx.set('Cache-Control', 'no-cache')
ctx.set('Content-Type', parseMime(url))
// Compare time ,mtime Is the last modification time of the file
if (ifModifiedSince === fileStat.mtime.toGMTString()) {
ctx.status = 304
} else {
ctx.set('Last-Modified', fileStat.mtime.toGMTString())
ctx.body = await parseStatic(filePath)
}
}
})
On the first request , In the response header :
On second request , Request header :
Because the resource has not been modified , Then hit cache , return 304:
At this point, let's modify index.css
.box {
width: 500px;
height: 300px;
background-image: url('../image/guang.jpg');
background-size: 100% 100%;
/* Modify here */
color: #333;
}
Then let's refresh the page , index.css
Changed , So will Miss cache
, return 200 And new resources , and guang.jpg
No modification , be Hit cache
return 304:
Etag,If-None-Match
Actually Etag,If-None-Match
Follow Last-Modified,If-Modified-Since
Roughly the same , The difference lies in :
- The latter is the last modification time of the comparison resource , To determine whether the resource has been modified
- The former is to compare the content of resources , To determine whether the resource is modified
How do we compare the content of resources ? We just need to read the content of the resource , Turn into hash value , Just compare it before and after !!
const crypto = require('crypto')
app.use(async (ctx) => {
const url = ctx.request.url
if (url === '/') {
// Access the root path and return index.html
ctx.set('Content-Type', 'text/html')
ctx.body = await parseStatic('./index.html')
} else {
const filePath = path.resolve(__dirname, `.${url}`)
const fileBuffer = await parseStatic(filePath)
const ifNoneMatch = ctx.request.header['if-none-match']
// Production content hash value
const hash = crypto.createHash('md5')
hash.update(fileBuffer)
const etag = `"${hash.digest('hex')}"`
ctx.set('Cache-Control', 'no-cache')
ctx.set('Content-Type', parseMime(url))
// contrast hash value
if (ifNoneMatch === etag) {
ctx.status = 304
} else {
ctx.set('etag', etag)
ctx.body = fileBuffer
}
}
})
The verification method is the same as just Last-Modified,If-Modified-Since
The same as , I won't repeat it here ...
summary
Reference material
Conclusion
I'm Lin Sanxin , An enthusiastic front-end rookie programmer . If you make progress , Like the front , Want to learn the front end , Then we can make friends , Fish together, ha ha , Fish schools , Add me, please note 【 Think no 】