preface

Hello everyone, I am Lin Sanxin, with the most easy to understand the most difficult knowledge points is my motto, the basis is advanced premise is my initial mind

background

HTTP caching is important both in development and in interviews for two reasons:

  • The development of: Rational utilizationHTTP cacheImproves front-end page performance
  • In the interview:HTTP cacheThis is a common interview question

So in this article, I will not talk nonsense, through Nodejs simple practice, to give you the most accessible HTTP cache, through this article will be sure to understand it!!

Lead to

To prepare

  • Creating a foldercache-studyAnd prepare the environment
npm init
Copy the code
  • The installationKoa, nodemon
npm i koa -D
npm i nodemon -g
Copy the code
  • createIndex.js, index.html, static folder
  • index.html
<! DOCTYPEhtml>
<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>
Copy the code
  • static/css/index.css
.box {
  width: 500px;
  height: 300px;
  background-image: url('.. /image/guang.jpg');
  background-size: 100% 100%;
  color: #000;

}
Copy the code
  • 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 the file
function parseMime(url) {
  // path. extName Obtains the file name extension 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 === '/') {
    // Accessing the root path returns index.html
    ctx.set('Content-Type'.'text/html')
    ctx.body = await parseStatic('./index.html')}else {
    const filePath = path.resolve(__dirname, `.${url}`)
    // Set the type
    ctx.set('Content-Type', parseMime(url))
    // Set up the transport
    ctx.body = await parseStatic(filePath)
  }
})

app.listen(9898.() = > {
  console.log('start at port 9898')})Copy the code

The launch page

Now you can enter nodemon Index in the terminal. If you see the display below, it means that the service has been successfully started

At this point, you can enter http://localhost:9898/ in the browser link, open the following page, it represents the page access success!!

HTTP cache types

There are two common types of HTTP caches:

  • Strong cache: Can be determined by either of these two fields
    • expires
    • Cache-control (higher priority)
  • Negotiate the cache: can be determined by one of these two pairs of fields
    • The if-modified-since last-modified
    • Etag, if-none-match (higher priority)

Strong cache

So let’s talk about strong caching

expires

We simply set the Expires in the response header to the current time + 30s

app.use(async (ctx) => {
  const url = ctx.request.url
  if (url === '/') {
    // Accessing the root path returns index.html
    ctx.set('Content-Type'.'text/html')
    ctx.body = await parseStatic('./index.html')}else {
    const filePath = path.resolve(__dirname, `.${url}`)
    // Set the type
    ctx.set('Content-Type', parseMime(url))
    // Sets the Expires response header
    const time = new Date(Date.now() + 30000).toUTCString()
    ctx.set('Expires', time)
    // Set up the transport
    ctx.body = await parseStatic(filePath)
  }
})
Copy the code

Then we refresh the front page, and we see an extra Expires field in the response header of the requested resource

And, in 30 seconds, after we refresh, we see that requests are going to memory, which means that setting expires to a strong cache is 30 seconds, and for 30 seconds, resources are going to the local cache without being re-requested

Note: You can check the Disabled cache, refresh it, and then uncheck the Disabled cache when your Nodejs code has updated the expiration date

cache-control

Cache-control works just as well as Expires, except that the fields are set to a different value, which is the number of seconds and the number of milliseconds

app.use(async (ctx) => {
  const url = ctx.request.url
  if (url === '/') {
    // Accessing the root path returns index.html
    ctx.set('Content-Type'.'text/html')
    ctx.body = await parseStatic('./index.html')}else {
    const filePath = path.resolve(__dirname, `.${url}`)
    // Set the type
    ctx.set('Content-Type', parseMime(url))
    // Set the cache-control response header
    ctx.set('Cache-Control'.'max-age=30')
    // Set up the transport
    ctx.body = await parseStatic(filePath)
  }
})
Copy the code

The front-end page response header has more cache-control field, and the local cache will be used for 30 seconds, and the server will not be requested

Negotiate the cache

Different from the strong cache, the strong cache does not go to the server but only to the local cache during the aging time. The negotiation cache is to go to the server side, if the request for a resource, to request the server, found that the cache will return 304, otherwise it will return the requested resource, then how to tell the fortune of the cache? So let’s talk about

The if-modified-since last-modified

To put it simply:

  • The first time a resource is requested, the server sends the requested resourceThe time was last changedIn the response headerLast-ModifiedIs sent to and stored in the browser
  • On the second request for the resource, the browser will treat the time just stored as the request headerIf-Modified-SinceThe server takes this time and compares it with the last modification time of the requested resource
  • The resource has not been modifiedHit the cache“, then return304If no, the resource has been modifiedDead in cache, returns the modified new resource
// 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 === '/') {
    // Accessing the root path returns 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))
    // Comparison 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)
    }
  }
})
Copy the code

On the first request, in the response header:

On the second request, in the request header:

Since the resource is not modified, the cache is hit and 304 is returned:

At this point, let’s modify index.css

.box {
  width: 500px;
  height: 300px;
  background-image: url('.. /image/guang.jpg');
  background-size: 100% 100%;
  /* Change this */
  color: #333;
}
Copy the code

Then we refresh the page, index.css has changed, so it will not be cached, returning 200 and the new resource, while guang.jpg has not changed, hitting the cache will return 304:

Etag, If – None – Match

Etag, if-none-match is the same as last-modified, if-modified-since.

  • The latter is to compare the last modification time of the resource to determine whether the resource has been modified
  • The former is to compare the content of the resource to determine whether the resource is modified

So how do we compare the content? We just need to read the content of the resource, hash it, and compare it to the previous one!!

const crypto = require('crypto')

app.use(async (ctx) => {
  const url = ctx.request.url
  if (url === '/') {
    // Accessing the root path returns 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']
    // Produce the 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))
    // Compare hash values
    if (ifNoneMatch === etag) {
      ctx.status = 304
    } else {
      ctx.set('etag', etag)
      ctx.body = fileBuffer
    }
  }
})
Copy the code

Modified-since = last-modified = if-modified-since = last-modified = if-modified-since

conclusion

The resources

  • Blog.csdn.net/qq_32438227…

conclusion

I am Lin Sanxin, an enthusiastic front-end novice programmer. If you progress, like the front end, want to learn the front end, then we can make friends, touch fish ha ha, touch fish, point this –> touch fish boiling point