3. How to use HTTP caching (Expires, cache-control, Etag, last-modified) – which files require strong caching and which files require negotiated caching

Reference link 1: Thoroughly understand strong caching and negotiated caching

Reference link 2: Browser cache

Reference link 3: Kill 304, Cache-Control: immutable

How to build express or node service

### How to build express,
  npm install express --save

### How express implements hot update
  npm install node-dev -D
  
  // In the script in package.json, configure
  "dev": "node-dev ./bin/www"

  // Startup project
  npm rundev

### How to add get/post request in express
  It can be viewed under index.js in routes

  Note: The get request is opened at localhost:3000/ and the data can be accessed directly through the browser.

      Post requires postman or axios request to test whether the interface is normal. You cannot use localhost:3000/login to test the interface. The default is the get request method, which will report a 404 page does not exist.


1. Use express to simulate strong caching

const express = require('express');
const fs = require('fs');
const app = express();
const port = 3000;
const path = require('path');
var CacheControl = require("express-cache-control")
var cache = new CacheControl().middleware
// express-cache-control
// https://www.npmjs.com/package/express-cache-control

app.get('/',(req,res) => {<!-- -->
  // console.log('fs111', fs.readFileSync('./index.html'))
  const data = fs.readFileSync('./src/index.html')
  res.end(data)

})


// How does express introduce css files? An error message is reported. fs.readFile is not a function. How to solve it? ?
// app.use(express.static(path.join(__dirname, '/public')));
app.get('/style.css', cache('seconds', 10), (req,res) => {<!-- -->
  // res.setHeader('Expires',new Date(new Date().getTime() + 1000 * 10).toGMTString())
  // res.setHeader('Cache-Control', 'max-age=10')
  // res.setHeader('Content-type', 'text/css');
  const data = fs.readFileSync('./public/style.css')
  // console.log('fs222', data)
  // setTimeout(() => {<!-- -->
    // Put fs.readFilesync in the function to prompt that fs.readFilesync is not a function
    // const data = fs.readFilesync('./public/style.css');
    res.end(data);
  // },3000)
})


app.get('/settingPage.js', (req,res) => {<!-- -->
  res.setHeader('Expires',new Date(new Date().getTime() + 1000 * 10).toGMTString())
  res.setHeader('Cache-Control', 'max-age=10')
  const data = fs.readFileSync('./src/settingPage.js')
  // setTimeout(() => {<!-- -->
    res.end(data);
  // },3000)
})

app.listen(port,() => {<!-- -->
  console.log(`Example app listening at http://localhost:${<!-- -->port})`)
})

2. Use node to simulate strong caching

let http = require('http');

let server = http.createServer(function(request, response){<!-- -->
  console.count('request')
    let url = request.url,
        content = null;

    console.log('Received http protocol request, the request address is: ' + url);

    if(url == '/' || url == '/index.html'){<!-- -->
        content = '<link rel="stylesheet" href="/register.css">page content';
        // Why /register.css is called? Because href='/register.css' will request css, so it will be executed again.
        response.setHeader('Content-type', 'text/html');
    }
    else if(url == '/register.css'){<!-- -->
        content = 'body{color:red;}';
        response.setHeader('Cache-Control', 'max-age=10');
        response.setHeader('Content-type', 'text/css');
    }

    if(content === null){<!-- -->
        console.log(url + 'The address is invalid');
        response.writeHead(404, {<!-- -->
            'Content-type' : 'text/html'
        });
        response.end('404 Oh dear!');
    }else{<!-- -->
        response.end(content);
    }
});

server.listen(8282, 'localhost');
console.log('server start at http://localhost:8282');

3. Use negotiation cache ETag in Express. ETag: There is one for each file. It changes when the file is modified. It can look like md5

Reference address: https://juejin.cn/post/6844903763665240072

/*
browser cache
https://juejin.cn/post/6844903763665240072

Kill 304, Cache-Control: immutable
https://www.cnblogs.com/ziyunfei/p/5642796.html
*/

const express = require('express');
const fs = require('fs');
const app = express();
const port = 3000;
const path = require('path');
var CacheControl = require("express-cache-control")
var cache = new CacheControl().middleware
// express-cache-control
// https://www.npmjs.com/package/express-cache-control
const etag = require('etag')

app.get('/',(req,res) => {<!-- -->
  const data = fs.readFileSync('./src/index.html')
  const etagContent = etag(data); //Generate a unique identifier based on the file
  res.setHeader('Etag', etagContent) // Actually it is an MD5

  //Read the file fingerprint returned to the client for the first time
  // console.log('req-->', req.headers)
  const ifNoneMatch = req.headers['if-none-match'];
  console.log('res-->', res.statusCode)
  if (ifNoneMatch & amp; & amp; ifNoneMatch === etagContent) {<!-- -->
    console.log('Are they the same hash value?')
    return (res.statusCode = 304) & amp; & amp; res.end();
  }
  // console.log('fs111', fs.readFileSync('./index.html'))
  res.end(data)
})


// How does express introduce css files? An error message is reported. fs.readFile is not a function. How to solve it? ?
// app.use(express.static(path.join(__dirname, '/public')));
app.get('/style.css', cache('seconds', 10), (req,res) => {<!-- -->
  // res.setHeader('Expires',new Date(new Date().getTime() + 1000 * 10).toGMTString())
  // res.setHeader('Cache-Control', 'max-age=10')
  // res.setHeader('Content-type', 'text/css');
  const data = fs.readFileSync('./public/style.css')
  // console.log('fs222', data)
  // setTimeout(() => {<!-- -->
    // Put fs.readFilesync in the delayer function to prompt that fs.readFilesync is not a function. Just extract it outside settimeout.
    // const data = fs.readFilesync('./public/style.css');
    res.end(data);
  // },3000)
})

app.get('/settingPage.js', (req,res) => {<!-- -->
  res.setHeader('Expires',new Date(new Date().getTime() + 1000 * 10).toGMTString())
  // res.setHeader('Cache-Control', 'max-age=10')
  const data = fs.readFileSync('./src/settingPage.js')
  // setTimeout(() => {<!-- -->
    res.end(data);
  // },3000)
})

app.listen(port,() => {<!-- -->
  console.log(`Example app listening at http://localhost:${<!-- -->port})`)
})
The first request status code is 200, set etag in res

Refresh the request again, get if-none-match in req and it is the same as etag, set statusCode = 304,

4. The negotiated cache last-modified is used in Express. The last modified time of the file. After the server receives the request and finds if-modified-since, it will be compared with the last modified time of the requested resource (Last-Modified). If If the last modification time is newer (larger), it means that the resource has been modified again, and the latest resource will be returned, HTTP 200 OK; if the last modification time is older (smaller), it means that the resource has no new modifications, and HTTP 304 will be returned to cache.

The first request status code is 200, set last-modified in res

Refresh the request again, get if-modified-since in req and it is the same as last-modified, set statusCode = 304,

/*
browser cache
https://juejin.cn/post/6844903763665240072

Kill 304, Cache-Control: immutable
https://www.cnblogs.com/ziyunfei/p/5642796.html
*/

const express = require('express');
const fs = require('fs');
const app = express();
const port = 3000;
const path = require('path');
var CacheControl = require("express-cache-control")

// cache package
var cache = new CacheControl().middleware
// express-cache-control
// https://www.npmjs.com/package/express-cache-control


// Negotiate cache Etag, first convert the file into buff type, then use etag to generate a unique hash value from the file content, and finally obtain the etag value by getting if-none-match for comparison.
const etag = require('etag')
// etag reference address: https://blog.csdn.net/weixin_42989576/article/details/123695991

// Negotiate cache last-modified, check the last modified time of the file through stat, if last-modified !== if-modified-since, then modify statusCode = 304
const {<!-- -->stat,readFile} = require('fs/promises');

app.get('/',async (req,res) => {<!-- -->
  const data = fs.readFileSync('./src/index.html')
  const statobj = await stat('./src/index.html');
  console.log('3838383',statobj)
  const ctime = statobj.ctime.toGMTString();
  res.setHeader('Last-Modified',ctime);
  const modifiedSince = req.headers['if-modified-since'];
  if (modifiedSince & amp; & amp; modifiedSince === ctime) {<!-- -->
    console.log('The last modified times of the files are equal')
    // After receiving the request, the server finds if-modified-since and compares it with the last modification time of the requested resource (Last-Modified). If the last modification time is newer (larger), it means that the resource has been modified again, and the latest is returned. Resource, HTTP 200 OK; if the last modification time is older (smaller), it means that the resource has no new modifications, and HTTP 304 will be responded to the cache.
    // Link: https://juejin.cn/post/6844903838768431118
    return (res.statusCode = 304) & amp; & amp; res.end();
  }
  res.end(data)
})




// How does express introduce css files? An error message is reported. fs.readFile is not a function. How to solve it? ?
// app.use(express.static(path.join(__dirname, '/public')));
app.get('/style.css', cache('seconds', 10), (req,res) => {<!-- -->
  // res.setHeader('Expires',new Date(new Date().getTime() + 1000 * 10).toGMTString())
  // res.setHeader('Cache-Control', 'max-age=10')
  // res.setHeader('Content-type', 'text/css');
  const data = fs.readFileSync('./public/style.css')
  // console.log('fs222', data)
  // setTimeout(() => {<!-- -->
    // Put fs.readFilesync in the function to prompt that fs.readFilesync is not a function
    // const data = fs.readFilesync('./public/style.css');
    res.end(data);
  // },3000)
})


app.get('/settingPage.js', (req,res) => {<!-- -->
  res.setHeader('Expires',new Date(new Date().getTime() + 1000 * 10).toGMTString())
  // res.setHeader('Cache-Control', 'max-age=10')
  const data = fs.readFileSync('./src/settingPage.js')
  // setTimeout(() => {<!-- -->
    res.end(data);
  // },3000)
})

app.listen(port,() => {<!-- -->
  console.log(`Example app listening at http://localhost:${<!-- -->port})`)
})

last-modified has the following disadvantages: You may think that using Last-Modified is enough to let the browser know whether the local cache copy is new enough, why do you need Etag? The emergence of Etag in HTTP1.1 (that is to say, ETag is new, in order to solve the shortcomings of only If-Modified before) is mainly to solve several problems that are difficult to solve with Last-Modified:
  1. Some files may change periodically, but their content does not change (only the modification time changes). At this time, we do not want the client to think that the file has been modified and re-GET;
  2. Some files are modified very frequently, such as modifications in less than seconds (for example, N times in 1 second). The granularity that If-Modified-Since can check is s-level, and this kind of modification cannot be judged (or It is said that UNIX records MTIME can only be accurate to seconds);
  3. Some servers cannot accurately obtain the last modification time of a file.

Problems encountered 1. Express/node Why Cache-Control does not take effect? Reason: The browser sidebar (maybe Deactivate cache is checked) will cause the cache to not take effect. Every time http is sent Request)

Problem 2 encountered, when fs.readFilesync is placed in the delayer function, it prompts that fs.readFilesync is not a function, just extract it outside settimeout

Problem 3 encountered: How to set the value of etag. You need to npm install etag, then use the file to generate a hash value, and finally compare it. If the value is the same, modify the code value.

Problems encountered 4. How to set the value of last-modified

What is the impact of user behavior on caching? ? ?

  • That is: F5 will skip the strong cache rules and go directly to the negotiated cache;; Ctrl + F5 will skip all cache rules and re-obtain resources just like the first request.

Project caching strategy

  • For example, in the vue project, the scaffolding has hashed the changed files, so we don’t need to operate the general js and css files.
  • For index.html, we need to do no-store processing on nginx, that is, not cache index.html at all, and request the latest html every time. . . Because html will externally link css and js. If my html is still cached, the link will still be the old css. Think about it? ? ?

No need to cache, request the latest every time? ? ?

  • Login is a QR code. The latest one needs to be generated every time, so no cache is used.
  • The avatar after logging in needs to be the latest one every time, so no cache is used.

Which files require strong caching?

  • index.html page, if script or link is introduced, caching can be used and there will be almost no change.

  • The background image or QR code of the login page is fixed and does not change, and strong caching is used (because it is rarely updated)

  • css files, system default, cdn download, are placed in strong cache

Which files need to be negotiated for caching?

  • In the index.html page, if multiple css/js/ are introduced, when index.html uses negotiated caching, the content (hash value) of css/js changes. At this time, index.html still refers to the old css/js, so index If the js hash value introduced in .html changes, negotiation caching must be used.
  • js file, as long as the content changes, it needs to be updated. Use etag and last-modified to negotiate the cache.

Will the forced cache and negotiated cache be stored in the memory cache or the hard disk cache? Can you give me some advice?

First of all, cache data will definitely be stored on the hard disk. Secondly, some resources will be stored in memory after the browser loads them, usually smaller non-asynchronously loaded files. When resources are loaded, the memory cache will be fetched first. If the memory cache does not exist, the hard disk cache will be fetched. The memory cache will be cleared after the web page is closed.