前端浏览器缓存优化:提高网站访问速度的重要手段

背景

目前越来越多的网站和应用程序依赖于前端技术。在前端技术中,浏览器缓存是一项重要的优化技术,可以显著提高网站的性能和用户体验。通过利用浏览器缓存,可以减少页面请求次数,降低服务器的负载,提高网站的访问速度,从而提高用户的体验。

在本文中,我们将探讨前端浏览器缓存的优化技术以及它们的实现方式

如果您需要进一步的协助或有任何问题,请随时提问!希望这篇文章对大家有所帮助。

什么是浏览器缓存

浏览器缓存是指浏览器在第一次访问一个网页时,将页面的各种资源文件(如样式表、JavaScript文件、图片等)缓存到本地磁盘上。当用户再次访问该网页时,浏览器会先检查本地缓存是否存在对应的资源文件,如果存在未过期,就直接从本地加载资源文件,而不是重新下载。

浏览器缓存优化是一种可以显著提高网站性能的技术,因为它减少了网络请求次数,降低了服务器的负载,同时也缩短了用户等待页面加载的时间。

浏览器缓存机制

浏览器缓存位置

当我们访问页面后,再次刷新页面,打开浏览器开发者工具 Network,可以发现很多资源文件都来自缓存。例如:

或者:

浏览器缓存分为四个位置:

1、Memory Cache

内存缓存,将请求结果缓存到内存中,读取速度快。

Memory Cache是指浏览器在内存中缓存的一些静态资源,例如HTML、CSS、JavaScript文件等。由于内存读取速度非常快,因此浏览器将这些资源缓存在内存中,以便下次访问时能够更快地获取这些资源。缓存在内存中的资源可以非常快速地被访问和加载,但是内存空间非常有限,所以这些资源只会在浏览器打开期间保存。

2、Disk Memory

硬盘缓存,将请求结果缓存到硬盘中,读取速度相对慢。

Disk Cache 是指浏览器在磁盘中缓存的一些静态资源,例如HTML、CSS、JavaScript文件等。与 Memory Cache 不同,Disk Cache 中的资源可以被长时间保存,即使浏览器关闭后也可以再次访问。但是,由于磁盘读取速度相对内存较慢,所以从磁盘中读取缓存的资源会比从内存中读取慢一些。通常情况下,浏览器会根据 HTTP 响应头中的缓存控制信息来决定是否将数据缓存到硬盘中。

硬盘缓存比内存缓存读取速度慢,读取需要对硬盘进行I/O操作,会导致重新解析缓存内容,造成读取路的复杂。

我们常见最多的就是 Memory Cache 与 Disk Memory。

区别 Memory Cache Disk Memory
存储资源 一般脚本、图片、字体 一般非脚本css等
存储时效 进程关闭清除 不受进程关闭影响,可以缓存较长时间
存储空间
读取速度

CSS文件加载一次就可以渲染,不会频繁的读取,存储在Disk Cache;js脚本可能会随时会被执行,存储在Memory Cache,若存储在硬盘中,会因为I/O开销大导致浏览器失去响应。

3、Server Worker

可以让网站在用户离线时继续运行,并且可以控制页面的缓存。

Service Worker 是一种运行在后台的 JavaScript 脚本,可以控制网络请求和响应。Service Worker可以缓存页面中的资源,例如 HTML、CSS、JavaScript文件、图像、字体等,可以使网站在离线状态下仍能够访问。

与 Memory Cache和Disk Cache不同,Service Worker Cache是一种持久化的缓存,即使用户关闭浏览器后,这些缓存也可以被保留下来。

4、Push Cache

仅适用于HTTP/2,用于缓存推送的响应结果。

推送缓存,是HTTP/2的内容,并没有严格执行HTTP头部的缓存指令。在Server Worker、Memory Cache、Disk Cache都没有命中的时候,它会被使用。在Session中存在,Session结束就会被释放,缓存时间短暂。

总之,Memory Cache 和 Push Cache 缓存都是相对较短的缓存,一般只在浏览器当前打开的标签页中有效。而 Disk Cache 和 Service Worker 缓存则可以在用户关闭浏览器后仍然有效。

以下是将浏览器缓存位置四类的区别按照表格进行展示:

缓存位置 作用 存储时机 过期机制
内存缓存 临时存储,提高用户体验 tab页面关闭后自动清除 无过期机制,由浏览器自动控制
磁盘缓存 加快网页打开速度 长期存储,直到缓存满或过期 由服务器设置响应头进行控制
Service Worker 缓存 支持离线访问和更好的缓存控制 由开发者手动控制 由开发者手动控制
Push Cache 减少网络传输和服务器负载 由服务器设置响应头进行控制 由服务器设置响应头进行控制

说完浏览器缓存位置四类的区别,我们再来说一下prefetch cache 预取缓存

prefetch cache 预取缓存

预取缓存是在页面加载时自动预取相关资源,以便在用户需要时能够快速加载。它主要用于加速页面和资源的加载速度,减少用户等待的时间。

预取缓存的实现通常通过添加 link 标签来完成。通过标记prefetch实现预加载,被标记为 prefetch 的资源会在浏览器空闲的时间加载。例如,可以在页面的头部添加以下代码:

html 复制代码
<link rel="prefetch" href="https://example.com/image.jpg">

预取缓存(Prefetch Cache)与浏览器缓存位置四类有着一定的区别,下面是它们之间的主要区别:

区别 预取缓存 浏览器缓存位置四类
作用 加速页面和资源的加载速度 减少网络传输和服务器负载,提高用户体验
缓存位置 预取缓存中 内存缓存、磁盘缓存、HTTP 缓存、Service Worker 缓存
缓存内容 缓存页面和资源的 URL 缓存页面和资源的实际内容
控制方式 通过添加 link 标签来控制 通过 HTTP 头信息来控制

浏览器缓存分类

浏览器缓存分为强缓存和协商缓存两种。

强缓存

强缓存是指直接从缓存中读取资源,而不需要向服务器发送请求,一般通过设置响应头来实现。

常见响应头设置的缓存标识有 Cache-ControlExpires

Cache-Control字段用于控制缓存的行为,它可以设置多个指令,常用的有以下几个:

指令 作用
public 允许所有用户缓存
private 只允许私有缓存,比如浏览器缓存
no-cache 不直接使用缓存,需要先向服务器验证资源是否过期
no-store 不缓存资源

例如,设置强缓存的有效期为3600秒,可以添加如下的HTTP响应头:

arduino 复制代码
Cache-Control: max-age=3600

Expires字段用于设置资源的过期时间,它的值为一个GMT格式的日期时间字符串。如果当前时间在Expires字段的值之前,那么缓存资源就是有效的。

例如,设置缓存的过期时间为2024年1月1日,可以添加如下的HTTP响应头:

yaml 复制代码
Expires: Mon, 1 Jan 2024 00:00:00 GMT

协商缓存

协商缓存则是先向服务器发送请求,服务器根据请求头中的信息判断资源是否可以缓存,并返回相应的状态码和响应头信息,以告诉浏览器是否可以使用缓存。如果文件没有发生变化,则返回304状态码。

协商缓存可以通过设置HTTP响应头中的Last-ModifiedETag字段来实现。

Last-Modified字段用于记录资源的最后修改时间,它的值为一个GMT格式的日期时间字符串。当浏览器再次请求该资源时,会在请求头中添加If-Modified-Since字段,其值为上一次请求返回的Last-Modified字段的值。如果资源的最后修改时间比If-Modified-Since字段的值要新,那么服务器就返回最新的资源,否则返回304状态码。

例如,设置资源的最后修改时间为2022年1月1日,可以添加如下的HTTP响应头:

yaml 复制代码
Last-Modified: Fri, 1 Jan 2022 00:00:00 GMTyaml

ETag字段用于记录资源的唯一标识符,它的值可以是一个哈希值或者一个版本号。当浏览器再次请求该资源时,会在请求头中添加If-None-Match字段,其值为上一次请求返回的ETag字段的值。如果资源的唯一标识符和If-None-Match字段的值相同,那么服务器就返回304状态码,否则返回最新的资源。

例如,设置资源的唯一标识符为"abc123",可以添加如下的HTTP响应头:

vbnet 复制代码
ETag: "abc123"

强缓存和协商缓存区别

注意,强缓存和协商缓存是HTTP协议中的两种不同的缓存机制,它们的具体实现方式可能因浏览器和服务器的不同而有所差异。

特征 强缓存 协商缓存
缓存判断位置 客户端浏览器 服务端
缓存命中时机 在第一次请求资源时,服务器将资源返回给客户端,并在响应头中添加缓存标识 在第一次请求资源时,服务器将资源返回给客户端,并在响应头中添加缓存标识,客户端在后续请求时将该标识带回给服务器,服务器根据标识判断是否命中缓存
命中缓存的响应码 200(from cache) 304(not modified)
缓存过期时间 可以通过设置Expires或Cache-Control响应头来指定 可以通过设置Last-Modified或Etag响应头来指定
缓存优先级
缓存机制 客户端控制 服务器控制

强缓存和协商缓存的区别,也说明了两者在浏览器缓存机制下也存在着不同,见下面浏览器缓存过程

浏览器缓存过程(很重要

搞明白这个,基本就摸清了浏览器缓存的机制。

浏览器缓存的过程一般分为以下几步:

  1. 浏览器第一次(或禁止缓存)请求加载资源,向服务端发送请求,服务器返回200,浏览器将最新的资源从服务器下载。如果服务端配置了 Headers,服务端会在请求返回时,随 Response Headers 一起返回给浏览器,浏览器会将 Response Headers 与返回时间一起缓存。
  2. 浏览器再次发起请求加载资源的时候,会比较与上一次下载资源的时间差,如果没有超过 Cache-Control 设置的 max-age ,则没有过期,此时就会从本地缓存读取资源。如果浏览器不支持 HTTP1.1,那么则会用 Expires 判断是否过期(这一过程称为强缓存)。
  3. 如果对比时间发现已过期,或者没有强缓存标识。服务器则会查看请求的 Headers 中 If-None-Match 的值,与该请求资源的 Etag 做比较,如果相同则代表资源没有发生改变,返回304。否则,直接返回新的资源,并返回200(这一过程称为协商缓存)。
  4. 如果服务器收到的请求 Headers 中,没有 Etag 值,则会读取 If-Modified-Since 和被请求文件的最后修改时间做对比,如果相同则代表没有发生改变,返回304。否则,直接返回新的资源,并返回200(这一过程称为协商缓存)。

下载资源的同时,在 Response Headers 中会携带 Etag (资源的唯一标识,资源发生改变,标识也随之改变)、Last-Modified(资源文件最后一次更改时间),而浏览器会把这两个保存下来。

而向服务端发送资源请求时,在 Request Headers 中,会把浏览器下载资源时缓存的 ETag 放到 If-None-Match 中,把保存的 Last-Modified 放到 If-Modified-Since 中发给服务端。

Etag -> If-None-Match

Last-Modified -> If-Modified-Since

DNS缓存与CDN缓存

除了浏览器缓存以外,还有DNS缓存和CDN缓存也可以优化网站的访问速度。

DNS缓存

DNS缓存是指浏览器在访问网站时,会将网站的 DNS 解析结果缓存到本地缓存中。

这样下次访问同一域名时,就可以直接使用本地缓存中的解析结果,而不需要重新进行 DNS 解析,从而加快了网站的访问速度。

CDN缓存

CDN 缓存是指使用 CDN 服务提供商提供的缓存机制,将网站的静态资源文件缓存到CDN节点上,以加速用户访问网站的速度。

用户下次再访问同一资源时,就可以直接使用CDN节点中的缓存文件,而不需要重新下载文件,从而提高网站的访问速度。

缓存优化方案

为了更好地利用浏览器缓存,提高网站的访问速度,我们可以采取以下缓存优化方案:

打包构建优化

在打包构建时,可以为静态资源文件添加 Hash 值或版本号,以便在文件内容变化时,可以强制浏览器重新加载新的文件。这样可以避免用户访问旧版本的文件,提高网站的访问速度。

例如,可以将样式表的 URL 修改版本号为:

ini 复制代码
<link rel="stylesheet" href="style.css?v=1.0">

当样式表的内容发生变化时,只需要将 URL 中的版本号修改为 2.0,浏览器就会认为是一个新的文件,重新下载并缓存。

相比于版本号,Hash 更为优秀。

现在很多打包工具都默认配置了 Hash。其原理在于,只要文件内容不变,文件名就不会变,这样大大提高了静态文件缓存的使用寿命。

但是问题也就出现了,以上的这种优化,大都用于除 html 文件外的其它静态资源文件(比如说以.js、.png...后缀结尾的文件),而客户端在访问页面时,浏览器加载 html 是不会带上所谓的 Hash 或者版本号的。这就导致了很多时候服务端资源文件更新了,但是客户端由于缓存,还在使用之前老 html 文件加载的静态资源。那么这个问题如何处理呢?

首先,我们可以使用上面缓存过程 提到的强缓存协商缓存,当服务端文件更新后,浏览器借用 Headers 标识来实现加载新资源。

另外,也可以通过设置 HTML 的 cache-control 实现。

html 复制代码
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Cache-Control" content="no-cache" />

这样可以告诉浏览器不要缓存 HTML 文件,从而确保每次访问页面时都会重新请求最新的 HTML 文件。但是需要注意的是,有些情况下这个方案会被服务端默认配置所覆盖

我们还可以尝试添加 Etag,但是这个方案有时有效果,有时没有效果。(实际中,某云厂家的资源未返回Etag)

因此,我们需要综合考虑,选择最适合项目的方案。在我的调试中,最稳妥的方案是:设置浏览器不缓存 HTML

设置浏览器不缓存,每次浏览器刷新都会拉取最新的 HTML 文件。而当 HTML 中加载的资源文件路径发生变化时,浏览器就会直接拉取最新的资源文件。但是这个方案也需要注意:由于每次都会请求新的 HTML,所以 HTML 只能加载资源路径,不能加载大量代码,否则页面渲染速度会变慢。

具体实现方案是,在服务端的 Nginx 中添加禁止缓存的 Header 配置,切记只对 HTML 设置禁止缓存。配置如下:

bash 复制代码
location / { 
    if ($request_filename ~* .*.(?:htm|html)$) {
        add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
    }
}

通过上述方案的使用,可以有效解决浏览器缓存资源带来的问题,确保用户访问的始终都是最新资源。

部署nginx代理配置(很有效

在处理前端浏览器缓存优化的过程中,我发现了一个问题:由于我们的项目使用微前端技术搭建,许多项目资源都在同一域名下,路径不同。如果按照之前的方式直接在 location 配置中添加 Header 缓存控制,所有项目的 HTML 都将被修改。

为了解决这个问题,我对路径进行了正则处理:

nginx 复制代码
location ~ ^/static/(admin1|admin2|admin3)/ { 
    if ($request_filename ~* .*.(?:htm|html)$) {
        add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
    }
}

admin1|admin2|admin3 是各应用项目路径,可以添加和修改。

在上面的配置中,我使用了以下响应头:

  • private 只能被终端用户的浏览器缓存,不允许 CDN 等对其缓存。会影响到 CDN 缓存命中率,但在量较小的情况下,也可以禁用。
  • no-cache 需要进行协商缓存,发送请求到服务器确认是否使用缓存。或者可以使用 max-age=0
  • no-store 完全禁用缓存。
  • must-revalidateno-cache 类似,强制到服务端验证,适用于一些特殊场景,例如发送了校验请求,但发送失败了之类。
  • proxy-revalidate 与上面类似,要求代理缓存服务验证有效性。

添加成功后,刷新浏览器效果如下:

到此为止,我已经成功地解决了我的项目中的浏览器缓存优化问题。

需要注意的是,配置完成后,页面第一次刷新后还是旧代码,这是正常情况(因为浏览器仍在使用之前的缓存),清除缓存然后重新刷新即可。

我按照上面配置,多次测试发现可行,目前为止没有发现问题。如果大家觉得还不稳妥,还可以通过添加 ExpiresCache-Control 等响应头,控制浏览器缓存。配置完成后的效果如下:

压缩资源文件

另外,使用gzip压缩功能可以将网站的资源进行压缩,从而减少文件的大小,加快文件的下载速度,提高网站的访问速度。

切记,使用 gzip 压缩功能。不仅构建打包工具需要配置,nginx 也需要配置。 nginx 通过在 http 块中添加以下配置实现:

bash 复制代码
http {
    gzip on; # 开启gzip压缩
    gzip_min_length 1k; # 设置gzip压缩的最小文件大小
    gzip_buffers 16 8k; # 设置gzip压缩的缓存区大小
    gzip_http_version 1.0; # 设置gzip压缩使用的HTTP协议版本
    gzip_types text/plain text/css application/json application/javascript application/xml; # 设置需要gzip压缩的文件类型
}
  • 配置gzip压缩的文件类型,通过在gzip_types中添加需要进行gzip压缩的文件类型即可,例如上面的配置中,text/plain、text/css、application/json、application/javascript和application/xml这些文件类型会进行gzip压缩。
  • 配置gzip压缩的缓存区大小和最小文件大小,通过在gzip_buffers和gzip_min_length中设置缓存区大小和最小文件大小来控制gzip压缩的性能和效果。
  • 配置gzip压缩使用的HTTP协议版本,通过在gzip_http_version中设置gzip压缩使用的HTTP协议版本来控制gzip压缩的兼容性和稳定性。

配置成功如下:

其他常用缓存方案

使用DNS缓存和CDN缓存

除了浏览器缓存之外,DNS缓存和CDN缓存也是优化网站访问速度的重要手段。

DNS缓存可以加速域名解析,CDN缓存可以加速网站的内容分发。使用CDN可以将网站的静态资源文件缓存到CDN节点中,从而提高网站的访问速度。

在使用这些缓存技术的同时,还需要注意缓存的更新策略,避免出现过期缓存的情况。

我遇到过资源发布不更新问题,定位挺长时间,没有找到原因,最后找后端更新 CDN 才解决。

减少HTTP请求次数

减少HTTP请求次数可以减少网站的下载时间,从而提高网站的访问速度。可以通过合并文件、压缩文件等方式来减少HTTP请求次数。

减少重定向次数

重定向会增加网站的加载时间,从而降低网站的访问速度。可以通过合理的URL设计、避免链式重定向等方式来减少重定向次数。

除此之外还可以通过使用HTTP/2协议多路复用、二进制协议、头部压缩等功能、使用Memcached、Redis等缓存技术来优化网站的访问速度。

结语

总之,浏览器缓存优化可以大大提高网站的访问速度,减少服务器负载,提高用户体验。采取合适的缓存优化方案可以帮助我们更好地利用浏览器缓存,提高网站的性能和用户体验。

相关推荐
喵叔哟9 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django