前端缓存知多少

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

在现代 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开发中缓存设置的黄金标准,完美兼顾了性能体验和更新需求

相关推荐
再吃一根胡萝卜21 分钟前
08 · 前端:Vue3 + SSE 流式与执行链路可视化
面试
再吃一根胡萝卜25 分钟前
03 · 后端:FastAPI 与分层架构
面试
mldong2 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排3 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60613 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角4 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!4 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰5 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
Interview Aid1125 小时前
Walmart Global Tech SDE 三轮面经|基础、并发、压力面
面试·职场和发展
mayaairi6 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js