What you might need to know about browser caching

browser cache

The principle of caching is to store a copy of the response of the requested resource after the first request. When the user initiates the same request again, if the cache hits, the previously stored copy is directly returned to the browser to avoid re-initiating resource requests to the server.

image.pngHTTP caching is the most commonly used caching technology in our daily development. HTTP cache can be divided into strong cache and negotiation cache according to whether the browser needs to initiate a request to the server to inquire about cache related information when judging the cache hit.

Strong cache

The caching strategy is implemented by setting HTTP headers. The header fields related to strong caching are Expires and cache-control.

Expires is a field that controls the cache expiration date and timestamp in the HTTP1.0 protocol. It is an absolute time. When the client's local time exceeds the Expires time, it means that the cache expires.

Expires: Mon, 21 Apr 2031 01:13:16 GMT
复制代码

Cache-Control is a field that controls the cache in the HTTP1.1 protocol. The max-age attribute of Cache-Control controls the validity period of the resource cache, which is a time length in seconds. Valid for 315360000 seconds.

Cache-Control: max-age=315360000
复制代码

Cache-Control can also configure some other property values ​​to control caching:

  • no-store: It is forbidden to use any caching strategy, and every request goes to the server to get the latest resources.
  • no-cache: Each time a request is initiated, it will not directly enter the expiration check of the strong cache, but directly negotiate with the server to verify the validity of the local cache, that is, take the negotiated cache route to be discussed below.
  • private: The resource can only be cached by the browser, the default value of Cache-Control.
  • must-revalidate: Cacheable but must revalidate with the server.

(The following are the settings for the proxy server cache)

  • public: The resource can be cached by both browsers and proxy servers.
  • s-maxage: The expiration time of the cache in the proxy server.
  • proxy-revalidate: Require the proxy server to re-validate the cached response.

In HTTP1.1, Cache-Control is a complete replacement for Expires. Generally speaking, it is enough to use it in project practice. Continue to use Expires for compatibility with HTTP1.0.

Negotiate cache

当 Cache-Control 校验缓存过期,或者设置了no-cache时,浏览器就会与服务器进行协商,判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。

跟协商缓存相关的 Header 字段有Last-Modified / If-Modified-SinceETag / If-None-Match (分别成对出现)

Last-Modified / If-Modified-Since

在每次服务器返回资源的同时,会在响应头中同时返回Last-Modified字段,它是一个时间戳,表示资源最近一次的修改时间。

last-modified: Mon, 11 Jan 2021 08:15:02 GMT
复制代码

随后我们每次需要发送请求时,会带上一个叫 If-Modified-Since 的时间戳字段,它的值就是上一次 response 返回的 Last-Modified 值。

if-modified-since: Mon, 11 Jan 2021 08:15:02 GMT
复制代码

服务器接收到这个时间戳后,会比对该时间戳和资源在服务器上的最后修改时间:

  • 如果两者不同,返回200和完整的响应内容。
  • 如果两者相同,返回304和空的响应体,直接从缓存读取资源。

需要注意的是,强缓存命中对应的状态码是200,协商缓存命中对应的状态码是304。

ETag / If-None-Match

Etag 是由服务器为每个资源生成的唯一的标识字符串,类似于文件指纹。和 Last-Modified 类似,当首次请求时,会在响应头返回Etag字段。

etag: "88158AFCFF1E7F4B8B00A1BA81171B61"
复制代码

下一次请求时,会将之前响应头中Etag的值作为此次请求头中If-None-Match字段,提供给服务器进行校验。 若资源没有更新,就返回304和空的响应体,直接从缓存读取资源。否则,就会返回返回更新后的资源并且将新的缓存信息一起返回。

Etag 在感知文件变化上比 Last-Modified 更加准确,优先级也更高。当 Etag 和 Last-Modified 同时存在时,以 Etag 为准。

应用场景

html文档一般会设置 no-cache。在meta中设置:

<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="cache-Control" content="max-age=0">
复制代码

CSS,JS,图片等静态文件一般使用强缓存,设置较长的过期时间,并且文件名添加hash。比如掘金的文章详情页,js文件的缓存过期时间为max-age=31536000(一年)。

image.png

同时文件名必须添加hash,这样当文件名hash发生变化,则视为新的资源,从服务端直接获取。这样可以减少大量的静态资源的网络请求,由新hash标志的文件也不会因为缓存的原因滞后。

通过webpack,我们可以方便地为文件名添加hash。

用webpack为文件名添加hash

文件指纹是一串哈希符,通常是加载文件名后面,用于标识文件,做版本的管理。比如项目上线后,需要修改一些东西,只需要发布修改过的文件,对于没有修改过的文件,用户在访问的时候,依旧可以使用浏览器缓存,加速页面渲染。

webpack中的文件指纹有三种:

  • hash:和整个项目的构建相关,所有文件共用一个hash值,任何文件有修改,项目的hash值就会更改,没有实现缓存的效果。
  • chunkhash:根据不同的入口文件(entry)进行依赖文件解析、构建对应的chunk,生成对应的chunkhash值
  • contenthash: 由文件内容产生的hash值,内容不同产生的contenthash值也不一样。

设置文件指纹的通常是静态资源文件,以下是几种文件的文件指纹设置方法:

  • js:设置output的filename,使用[chunkhash]
output: {
   path: path.join(__dirname,"/dist"),
   filename: "js/bundle.[chunkhash:6].js"
}
复制代码
  • css:只有将css抽离成单独的文件时才需要设置指纹,借助mini-css-extract-plugin将css提取成独立的文件,设置mini-css-extract-plugin的filename。使用[contenthash],如果只修改js就可以不用重新上传css文件。
new MiniCssExtractPlugin({
    filename: 'css/[name].[contenthash:7].css'
})
复制代码
  • 图片:设置file-loader或url-loader的name,使用[hash],与上面整个项目构建的hash不同,图片等媒体文件的hash是根据文件的内容所生成的,换成[contenthash]也可以。
{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'file-loader', 
    options: {
    	esModule: false,
        name: 'img/[name].[hash:7].[ext]'
    }
}
复制代码

In the case of using hot update, chunkhash or contenthash cannot be used, an error will be reported, and only hash can be used. Therefore, file fingerprints are generally not set in the development environment, but are set in the production environment.

Guess you like

Origin juejin.im/post/7084864717147078663