前端缓存知多少

一、引言:缓存是前端性能的核心

在现代 Web 应用中,缓存几乎是影响前端性能的最关键因素。从用户体验出发,页面加载速度直接决定了用户的停留时长与转化率。研究显示,页面每延迟一秒,转化率就可能显著下降。而像 Lighthouse 评分、核心 Web 指标(Core Web Vitals:LCP、FID、CLS)等性能评估体系,也将"首屏渲染速度""交互延迟"等指标作为衡量体验的核心,这些都与缓存策略密切相关。

缓存的价值不止于用户体验,它还带来多维度的收益

  • 减少网络请求:命中缓存时,资源直接来自本地或中间层,不必每次都访问服务器。
  • 节省用户带宽:移动端用户尤其受益,重复访问时不会再消耗额外流量。
  • 减轻服务器压力:海量请求若能通过缓存拦截,后端就能专注处理真正的动态业务逻辑。

在这个体系中,前端并不是单纯的缓存消费者。借助构建工具(如 Webpack、Vite)、资源命名策略(如文件指纹 hash)、以及与服务器(Nginx、CDN)的协同配置,前端工程师往往是缓存策略的设计者与执行者。正是这种"前端定义缓存、全链路受益"的模式,让缓存成为前端性能优化的核心议题。

二、Http缓存机制详解

HTTP缓存时Web性能优化中最重要的环节之一,其核心目的就是尽可能的减少客户端向服务器发起请求,同时保证资源的更新和正确性。

HTTP 缓存机制大体可以分为两类:强缓存协商缓存

强缓存

强缓存命中时,浏览器不会与服务器发生任何请求,直接从本地缓存中读取资源,速度最快。

关键字:有expiresCache-Control

  1. expires时HTTP/1.0中的过期时间,指定一个绝对时间点,

    yaml 复制代码
    Expires: Wed, 21 Oct 2025 07:28:00 GMT

    缺点就是依赖客户端时间,如果客户端时间不准,可能会失效

  2. Cache-Control: max-age=seconds:HTTP/1.1 推荐用法,指定资源在多少秒内有效。

    ini 复制代码
    Cache-Control: max-age=31536000

    表示一年内缓存有效

    强缓存可以通过Network面板中看请求,一般都是 在"Size"列会显示 from memory cache(内存缓存) 或 from disk cache(磁盘缓存),并且Status列是 200

协商缓存

当强缓存过期后,浏览器会向服务器验证缓存是否任然有效。如果服务器告诉它还有效,就继续使用本地缓存,否则就返回新资源。

关键字有:Last-Modified / If-Modified-SinceETag / If-None-Match

  1. Last-Modified / If-Modified-Since是服务器加在响应头上,

    yaml 复制代码
    Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

    下次请求时,浏览器带上:

    yaml 复制代码
    If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT

    如果文件没有发生变化, 服务器返回 304 Not Modified,不再传输实体内容。

  2. ETag / If-None-MatchETag是资源的唯一标识,

    vbnet 复制代码
    ETag: "abc123"

    下次请求时,浏览器带上:

    sql 复制代码
    If-None-Match: "abc123"

    标识一致返回304,否则就返回新资源。

    ETag优先级高于Last-Modified

缓存的行为由服务器响应头和浏览器策略共同决定。常见的 Cache-Control 组合有:

  • Cache-Control: no-store → 不缓存,敏感数据场景。
  • Cache-Control: no-cache → 缓存但每次都要协商验证。
  • Cache-Control: public → 可以被任何中间层缓存(如 CDN)。
  • Cache-Control: private → 只能被浏览器缓存,CDN 不能缓存。
  • Cache-Control: max-age=0, must-revalidate → 马上过期,但必须重新验证。

一般设置缓存策略都是在nginx中进行配置

比如设置强缓存:

bash 复制代码
location /static/ {
    # 设置强缓存:1年内有效
    add_header Cache-Control "public, max-age=31536000";  # 31536000秒=1年
    expires 1y;  # 等效于Expires头,1年后过期
}

首次请求直接返回资源及缓存头,后续请求浏览器直接读取本地缓存,状态码 200 (from disk cache)200 (from memory cache) ,不会发送请求到服务器。

设置协商缓存

csharp 复制代码
location /dynamic/ {
    # 启用协商缓存(默认已支持,无需显式配置ETag)
    add_header Last-Modified "";  # 可选:覆盖默认的Last-Modified
    etag on;  # 启用ETag(默认开启)
}

首次请求服务器返回资源及 Last-ModifiedETag ,后续请求浏览器携带 If-Modified-SinceIf-None-Match请求头验证缓存 ,若资源未修改,服务器返回 304 Not Modified,浏览器继续使用缓存;若资源已修改,服务器返回新的资源

三、构建工具固化缓存策略

当在nginx设置强缓存,静态资源的有效期为一年时,期间如果要更新这些静态资源,应该怎么做呢,比较古老的做法就是手动在静态资源后面打上一个时间戳,每次发版都手动更新下,当url发生了变化,自然就会重新从服务端重新获取数据,但是这种做法还是非常的繁琐的,有了web构建工具,这些就变得非常的简单了

比如在webpack中:

ini 复制代码
// webpack.config.js
module.exports = {
    output: {
        filename: '[name].[contenthash].js', // 自动启用强缓存
    },
};

在vite中

arduino 复制代码
// vite.config.js
export default {
    build: {
        assetsDir: 'static',
    },
};

然后入口文件index.html通常是不带hash值的,通常设置为Cache-Control: no-cache 或 max-age=0,直接启用协商缓存,由服务端来判断入口文件是否发生了变更,如果更改了直接返回最新的,没有更改,就返回304。

四、理解nginx缓存

首先了解下nginx

Nginx(发音为 "engine-x")是一款高性能、开源的 Web 服务器反向代理服务器负载均衡器HTTP 缓存。它因其稳定性、丰富的功能集、简单的配置和极低的资源消耗而闻名,如今已成为全球最受欢迎的 Web 服务器之一。

正向代理与反向代理
  1. 正向代理:

    正向代理是一个位于客户端和目标服务器之间的服务器。客户端发送请求时,请求先到达正向代理服务器,然后由正向代理服务器转发到目标服务器。目标服务器看到的是来自代理服务器的请求,而不是直接来自客户端。

    经常使用nginx来正向代理前端请求处理跨域问题。

    一般科学上网、内网访问外网、公司内网统一出口,都是正向代理的典型应用场景。

  2. 反向代理

    反向代理是 服务器端的代理。客户端访问的目标就是代理服务器(比如 Nginx),由它把请求转发给后端真实服务器,再把结果返回客户端。

    常见的web利用nginx代理跨域就是反向代理的经典使用场景。

写这篇博客的起源就是,在业务开发过程中,遇到了这样一个场景:接口采用的是stream流式输出,后来遇到了这样一个问题:在接口传输过程中,后端通过日志发现数据已经发送了,但是前端解析时不同步,前端接受到的数据总是延后,后来就发现了这个东西,nginx缓冲。

接下来学习下nginx缓存和缓冲。

缓存

缓存的核心目的是减少重复计算和重复请求,将响应保存下来,以便在后续相同的请求中直接使用,从而提升服务器性能,

一般来说nginx主要有两类缓存,静态资源缓存和代理缓存

静态资源就是和前面的强缓存一样,把静态资源缓存到内存或者磁盘;而代理缓存就是当nginx作为反向代理时,可以缓存上游后端的响应,比如请求接口GET /api/data,后端返回一端JSON,nginx可以直接缓存,下一次请求就不用再走后端了,比如这样配置:

ini 复制代码
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
​
server {
    location /api/ {
        proxy_pass http://backend;
        proxy_cache my_cache;
        proxy_cache_valid 200 302 10m; # 成功和重定向缓存10分钟
        proxy_cache_valid 404 1m;      # 404缓存1分钟
    }
}

静态资源缓存一般都是存放再浏览器本地,这是常规强缓存的存储方式,nginx自己并不会把静态文件缓存起来,而是直接从磁盘读取,它可以缓存文件句柄和元信息,加快打开文件的速度,但不是内容缓存。总结一下:强缓存是将文件存放再浏览器本地,协商缓存才会用到nginx缓存,nginx缓存可以加快读取打开文件的速度。

缓冲

缓冲是请求和响应过程中的临时存储,优化网络传输,主要分为两类

  1. 请求缓冲

    Nginx 在收到客户端请求体(比如大文件上传)时,不会直接转发给后端,而是先放到本地缓冲区。

    控制指令:client_body_buffer_sizeclient_max_body_size。 如果缓冲区不够大,多余的部分回写道临时文件中

  2. 响应缓冲

    当 Nginx 从上游(后端)拿到响应数据时,会先放进缓冲区,再根据情况返回给客户端。 后端响应慢, nginx 可以先吞下,客户端不用直接受影响。避免边收边转发,提升性能。

这个缓冲主要是为了避免客户端长时间和服务端链接,占用后端的进程、线程,一般都是默认开启的,然后遇到了流式传输这种需要实时数据时,就需要手动关闭它

bash 复制代码
location /live {
    proxy_pass http://backend;
    proxy_buffering off; # 关闭缓冲,实现实时流
}
五、一套完整的前端缓存策略
静态资源缓存策略

静态资源指JS、CSS、图片、字体等版本不变则内容也不变的文件。对它们的最佳实践是:开启强缓存,并通过内容哈希实现"精确更新"

  1. 在构建工具中配置内容哈希

这是实现强缓存的前提。使用Webpack、Vite、Rollup等工具打包时,必须为文件名添加哈希。

Webpack配置示例:

ini 复制代码
// webpack.config.js
output: {
  filename: '[name].[contenthash].js', // 使用内容哈希
  chunkFilename: '[name].[contenthash].chunk.js',
  assetModuleFilename: 'assets/[name].[contenthash][ext]',
},
  • 效果 :文件内容任何微小变化都会生成一个全新的哈希值文件名,如 app.a1b2c3 .js -> app.d4e5f6 .js
  • 为什么? 因为全新的URL意味着全新的请求,从而绕过任何旧的缓存。这样你就可以放心地把旧文件缓存非常久的时间。
  1. 在Web服务器上配置强缓存头

为这些带哈希的静态资源设置超长的 Cache-Control 头。

Nginx配置示例:

ini 复制代码
server {
    listen 80;
    server_name yoursite.com;
​
    # 匹配带哈希的静态资源
    location ~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
        # 查找资源并设置缓存头
        root /path/to/your/static/files;
        # 设置强缓存:1年
        expires 1y;
        add_header Cache-Control "public, immutable, max-age=31536000";
    }
​
    # 其他配置,比如处理HTML...
}
  • public:允许浏览器和CDN等代理缓存。
  • max-age=31536000:缓存有效期一年(以秒为单位)。
  • immutable :明确告诉浏览器,这个资源在有效期内永不会改变,期间用户刷新页面时浏览器也不会发起请求验证(If-None-Match),极大提升刷新性能。
HTML文件的缓存策略

HTML文件通常是入口文件,它引用了带哈希的静态资源。如果HTML被强缓存了,用户就无法获取到新的资源链接。

策略:使用协商缓存或短暂的强缓存。

Nginx配置示例:

ini 复制代码
server {
    # ... 其他配置 ...
​
    # 处理HTML文件
    location / {
        root /path/to/your/html/files;
        index index.html;
​
        # 方式1:使用协商缓存 (推荐)
        add_header Cache-Control "no-cache";
​
        # 方式2:设置一个非常短的强缓存时间 (例如5分钟)
        # expires 5m;
        # add_header Cache-Control "public, max-age=300";
    }
}
  • no-cache不是不缓存 ,而是在使用缓存前必须向服务器验证 (发送带有If-None-Match的请求)。如果服务器返回304,则直接使用本地缓存。这保证了用户总能拿到最新的HTML。
  • 短时间强缓存:对一些变化不频繁的官网首页,可以设置几分钟的强缓存,在性能和新鲜度之间取得平衡。
API响应的缓存策略

API响应通常是动态的、个性化的,需要非常谨慎地设置缓存。

  1. 绝对私人数据 :使用 Cache-Control: no-store。不存储任何副本。

    nginx

    bash 复制代码
    # 在Nginx代理层或后端应用代码中设置
    add_header Cache-Control "no-store";
  2. 可共享的公共数据:对于一些更新不频繁的公共API(如新闻列表、配置信息),可以设置短暂的缓存,显著降低服务器负载。

    nginx

    bash 复制代码
    # 在Nginx中为特定API路径设置缓存
    location /api/public/news {
        proxy_pass http://backend;
        # 缓存10分钟,且仅限代理服务器缓存
        add_header Cache-Control "public, s-maxage=600";
    }
    • s-maxage:仅适用于代理缓存(如CDN、Nginx),浏览器会忽略它。这样可以在服务器层面加速,而不影响客户端获取最新数据。
  3. 用户相关的私人数据 :使用 private

    nginx

    arduino 复制代码
    add_header Cache-Control "private, max-age=3600";
    • private:表示响应只适用于单个用户的浏览器缓存,中间的代理服务器(CDN)不能缓存它。

总结一下:

  1. 前端构建

    • 对所有静态资源(JS, CSS, 图片等)使用 [contenthash] 命名。
    • 将静态资源与HTML文件分开部署(通常上传到CDN)。
  2. 服务器配置

    • 带哈希的静态资源 :→ Cache-Control: public, max-age=31536000, immutable (强缓存一年)
    • HTML文件 :→ Cache-Control: no-cache (总是协商验证)
    • API接口 :根据敏感性选择 → no-store(不缓存)或 private(私有缓存)或 public, s-maxage=600(公共代理缓存)
  3. 协作

    • 前端需要明确告知后端或运维不同资源所需的缓存策略。
    • 使用CDN时,前端构建的哈希命名和后端配置的缓存头同样适用,有时还需在CDN管理后台进行额外配置和"缓存净化"操作。

这套组合拳是现代Web开发中缓存设置的黄金标准,完美兼顾了性能体验和更新需求

相关推荐
Dolphin_海豚3 小时前
【译】Vue.js 下一代实现指南 - 下卷
前端·掘金翻译计划·vapor
Apifox3 小时前
理解和掌握 Apifox 中的变量(临时、环境、模块、全局变量等)
前端·后端·测试
小白_ysf4 小时前
阿里云日志服务之WebTracking 小程序端 JavaScript SDK (阿里SDK埋点和原生uni.request请求冲突问题)
前端·微信小程序·uni-app·埋点·阿里云日志服务
你的电影很有趣4 小时前
lesson52:CSS进阶指南:雪碧图与边框技术的创新应用
前端·css
Jerry4 小时前
Compose 延迟布局
前端
前端fighter4 小时前
Vue 3 路由切换:页面未刷新问题
前端·vue.js·面试
lskblog4 小时前
使用 PHP Imagick 扩展实现高质量 PDF 转图片功能
android·开发语言·前端·pdf·word·php·laravel
whysqwhw4 小时前
Node-API 学习二
前端
whysqwhw4 小时前
Node-API 学习一
前端