1. Introduction

Front-end caching is not the same as browser storage. Cache: Cache browser storage: sessionStorage, localStorage, Cookie, indexedDB…

What we call caching does not mean that the server itself “does the caching”, but through the server side to tell the browser whether to use caching, and how to use some rules

1.1 When will caching be performed

For a particular resource, such as index.html. If a caching policy is set, the browser caches the resource the first time it is requested. On subsequent requests, if the cache is not obsolete, the resource in the cache is taken and the status code is 200. How to determine whether a cache is outdated depends on the type of cache used.

1.2 Cache location: cache area

The browser has a cache. Memory cache is preferred, followed by disk cache.

1.3 Preparations

Here we use Node to write a static file server to see how the cache works. The directory structure is as follows:

const fs = require('fs');
const http = require('http');
const url = require('url');
const path = require('path');

// Write a simple static server
const server = http.createServer((req, res) = > {
    const { pathname } = url.parse(req.url);
    const filePath = path.join(__dirname, pathname);

    fs.stat(filePath, (err, stats) = > {
        if(err) {
            res.statusCode = 404;
            res.end('Not found! ');
        }
        if(stats? .isFile()) {/* Start: sets the cache policy */
            // Cache policy
            /* End: set cache policy */
        } else {
            res.statusCode = 404;
            res.end('Not found! ')}})}); server.listen(3000.console.log('server listening on 3000'));
Copy the code

2. Cache type

2.1 Forcing Caching

  • After accessing the server for the first time to get the data, within the expiration time will not repeat the request, but directly get the browser cache resources.
  • Note: Directly accessed resources are not cached, only referenced resources are cached.

Set head:

// After setting: All requests sent within 30 seconds after the first request are cached by the browser
res.setHeader('Cache-Control'.'max-age=30'); // max-age Indicates the relative time in seconds
res.setHeader('Expires'.new Date(Date.now() + 1000 * 30).toUTCString()); // Expires is set for an earlier browser version
Copy the code

Example:

// For the complete code, see example/qiangzhi_cache.js
/* Start: sets the cache policy */
    console.log(filePath)
    // After setting: All requests sent within 30 seconds after the first request are cached by the browser
    res.setHeader('Cache-Control'.'max-age=30'); // max-age Indicates the relative time in seconds
    res.setHeader('Expires'.new Date(Date.now() + 1000 * 30).toUTCString()); // Expires is set to an absolute time for an older browser
    fs.createReadStream(filePath).pipe(res);
/* End: set cache policy */
Copy the code

Result: Send the second request within 30 seconds

1) Node: Directly accessed resources are not cached, so they are returned by the node server.

2) Browser:

Defect:

  • After the set time, the request must go to the server. If the file is still unmodified, it will be wasted.
  • Before the set time, the file is modified, but still cached, which is inconsistent with the actual.

Usage scenario:

Things that need to be cached for a long time, like baidu’s logo

2.2 Negotiation Cache

2.2.1 What is negotiation Cache

The browser caches the file, but you have to ask the server every time you want to use the cache, and the server will tell you whether or not to use the cache. This is called a negotiated cache.

A request is sent to the server. The server determines whether the request matches the negotiated cache based on some parameters in the request header. If the request matches, the server returns a 304 status code with a new response header to inform the browser to read the resource from the cache.

Before we do negotiation caching, let’s turn off strong caching:

res.setHeader('Cache-Control', 'no-cache')
Copy the code
  • No-cache: Indicates that the browser still caches the file, but still accesses the server every time
  • No-store: Tells the browser directly not to cache files and requests the server every time

2.2.2 Negotiation cache – last-modified

Comparisons are made based on the modification time of resources.

Set head:

res.setHeader('Last-Modified', mtime)
req.headers['if-modified-since']
Copy the code

Example:

// See example/xieshang_lastmodified_cache.js for the full code
/* Start: sets the cache policy */
    console.log(filePath)
    res.setHeader('Cache-Control'.'no-cache');
    const mtime = stats.ctime.toUTCString();
    if(req.headers['if-modified-since'] == mtime) {
        res.statusCode = 304;
        res.end();
    } else {
        res.setHeader('Last-Modified', mtime)
        fs.createReadStream(filePath).pipe(res);
    }
/* End: set cache policy */
Copy the code

Operation process:

  • The browser requests for the first timeindex.htmlFile;
  • The server willIndex.html + time identifierTo return. The timestamp tells the browser that the file is the most recentModify the time;
  • The browser received a message from the serverIndex.html + time identifierAnd willindex.htmlStored in the cache;
  • When the browser requests it a second time, it will include the time stamp;
  • The server gets the request and compares itTime to identifyandindex.htmlTime of last modification. If yes, tell the browser that the file has not been modified, go to the browser cache, and set the status code to304.

1) Node:2) Browser: Second request

Defect:

  • The modification time is seconds. If the modification is made within 1s, the modification time is not considered to be modified.
  • You modify it once, you undo it, the time changes, but the file doesn’t change, so the browser thinks it changed;
  • CND distribution, put on other server time, file time may be different.

2.2.3 Cache Negotiation using Etag

To compensate for the last-modified approach, our solution is to generate an MD5 stamp based on the contents of the file. How to generate the MD5 stamp? We need to use one of Node’s core modules, crypto, which is dedicated to handling encrypted packages. The specific introduction is put in the back.

We use a header called Etag to tell the browser. The policy generated by Etag varies from factory to factory. In short, the digest generates an MD5 value based on the content of the file.

Set head:

res.setHeader('Etag', etag)
req.headers['if-none-match']
Copy the code

Example:

// See example/xieshang_etag_cache.js for the full code
/* Start: sets the cache policy */
    console.log(filePath)
    res.setHeader('Cache-Control'.'no-cache');
    const etag = crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('base64');
    if(req.headers['if-none-match'] == etag) {
        res.statusCode = 304;
        res.end();
    } else {
        res.setHeader('Etag', etag)
        fs.createReadStream(filePath).pipe(res);
    }
/* End: set cache policy */
Copy the code

Operation process:

The last-modified timestamp was replaced with an MD5 stamp, but the rest was the same.

1) Node:

2) Browser: Second request

3. Best practice: Coercion and negotiation coexist

4. Supplementary: encryption algorithm

Before I get into that, let me just tell you what an encryption algorithm is. We usually always say MD5 encryption, SHA1 / SHA256 encryption, in fact, are not very accurate, accurately speaking, MD5 is the abstract algorithm, SHA1 / SHA256 is the salt algorithm.

Encryption algorithm refers to the algorithm that can be decrypted after encryption, such as the well-known symmetric encryption algorithm (public and private keys).

4.1 MD5 Digest Algorithm

  • Can’t decrypt, the encryption is irreversible, so it’s not an encryption algorithm. (There is a lot of talk on the Internet about MD5 encryption algorithm, which is not true. Online said decryption is actually violent collision, professional point called collision library);
  • Has an avalanche effect, meaning that if the content changes a little, the MD5 digest value will be completely different;
  • Unsafe, easy to reverse solution. The solution is the following salting algorithm or multi-round MD5 digest.
crypto.createHash('md5').update('123').digest('base64')
Copy the code

4.2 Salt adding algorithm (such as SHA1 and SHA256)

  • It’s actually a summary algorithm; More salt value than the general abstract algorithm. In the encryption process, it’s based onAbstract + salt valueTo encrypt;
  • Salt value is generally stored in the server, others can not reach;
  • For example: ‘123’ + ‘salt value’ ==> Summary result.

5. How the pure front end handles caching (Webpack)

How do you get users to use the latest resource files in the first place after the update? The smart front end figured out a way to change the path of the static resources in the update version, so that there is no caching problem when accessing the resources the first time.

// The great webPack feature allows you to pack files with hash values.
entry: {main: path.join(__dirname,'./main.js'),
    vendor: ['react'.'antd']},output: {path:path.join(__dirname,'./dist'),
    publicPath: '/dist/'.filname: 'bundle.[chunkhash].js'
}
Copy the code

6. Code:

Code address: github.com/preciousonl…