浏览器缓存优先级

公众号:小博的前端笔记

核心原则:浏览器总是优先尝试使用最快、最接近的可用有效缓存,避免不必要的网络请求。

以下是浏览器缓存机制的详细优先级顺序(从最高到最低),以及关键细节:

1、Service Worker 缓存 (最高优先级)

  • 触发条件: 网站注册了 Service Worker,并且该 Service Worker 的 fetch 事件处理程序中有意拦截请求并返回缓存响应。

  • 机制:

    • Service Worker 作为浏览器和网络之间的代理运行。
    • fetch 事件中,你可以完全编程控制 如何响应请求:可以返回 CacheStorage (caches API) 中的缓存、发起网络请求、构造新响应,甚至返回占位符。
    • 策略完全自定义: 你可以实现 CacheFirst(优先缓存)、NetworkFirst(优先网络)、StaleWhileRevalidate(快速返回缓存后更新)、NetworkOnly(仅网络)等任何策略。
  • 优先级最高原因: Service Worker 能拦截所有 同源下的请求(包括页面本身、HTML、CSS、JS、图片、API 请求等)。它的 fetch 事件处理程序最先执行,如果它决定从自己的缓存中返回响应,浏览器将不会进行后续任何缓存检查或网络请求。

  • 面试要点: 强调其编程可控性最高拦截权。即使资源在 HTTP 缓存中过期,只要 Service Worker 决定返回缓存版本,浏览器就会使用它。它是实现离线应用、高级缓存策略(如后台更新)的核心。

2、Memory Cache (内存缓存)

  • 触发条件: 当前浏览会话(Tab 页)中已加载过的资源(如脚本、样式、图片)。通常是同一页面或之前页面加载的资源被再次请求。

  • 机制:

    • 资源被直接存储在系统内存 (RAM) 中。
    • 访问速度极快(比磁盘快几个数量级)。
    • 生命周期非常短:关闭 Tab 页通常会被清除。浏览器在内存紧张时也会主动清理。
    • 通常用于存储较小高频访问的资源(如当前页面引用的 JS、CSS、小图标)。大的图片/视频可能不会被放入内存缓存,或优先被清除。
  • 优先级原因: 速度最快。如果资源在内存中存在且有效(未过期),浏览器会毫不犹豫地使用它。

  • 面试要点: 强调速度优势临时性 。它是提升页面二次加载速度(尤其是刷新)的关键。开发者无法直接控制哪些资源进入内存缓存。

3、HTTP Cache (Disk Cache / 磁盘缓存)

  • 触发条件: 资源响应头中包含有效的缓存指令(如 Cache-Control, Expires, ETag, Last-Modified)。

  • 机制: 这是最核心、最常用、开发者可控度最高的缓存机制。

    • 资源被存储在硬盘上。

    • 容量大,生命周期长(按策略,可能跨会话、跨 Tab)。

    • 强缓存 (Freshness):

      • 浏览器检查 Cache-Control (优先级高) 和 Expires 头部,判断资源是否在有效期内 (max-age, s-maxage)。
      • 如果有效期内 (fresh),浏览器直接从磁盘加载资源,完全不发送任何网络请求! (状态码 200 from disk cache / from prefetch cache)。
    • 协商缓存 (Validation):

      • 如果强缓存失效 (stale),浏览器会发起一个条件请求 (Conditional Request)

      • 请求头带上之前响应中的 ETag 值 (If-None-Match) 或 Last-Modified 时间 (If-Modified-Since)。

      • 服务器检查资源:

        • 如果未修改:返回 304 Not Modified (空 body),浏览器从 HTTP 缓存加载资源 (状态码 200 from disk cache304)。
        • 如果已修改:返回 200 OK 和完整的新资源,浏览器使用新资源并更新缓存
    • 关键头部:

      • Cache-Control: 核心指令 (max-age=3600, no-cache, no-store, public, private, immutable, must-revalidate, stale-while-revalidate等)。
      • Expires (HTTP/1.0,被 Cache-Control 覆盖):绝对过期时间。
      • ETag / Last-Modified: 用于协商缓存验证。
      • Vary: 指定缓存变体(如按 User-Agent, Accept-Encoding 缓存不同版本)。
  • 优先级原因: 速度较快(比网络快),容量大,持久性强,是开发者主要优化的目标。当内存缓存没有命中(比如首次访问、内存已清除),浏览器就会检查 HTTP 缓存。

  • 面试要点: 这是必考重点 !必须清晰区分强缓存 (不发请求)和协商缓存 (发条件请求,可能返回 304)。深入理解 Cache-Control 常用指令的含义和组合。强调 no-store (完全不缓存) 和 no-cache (总是验证) 的区别。解释 ETag (通常更精确) 和 Last-Modified 的优缺点。

4、Push Cache (HTTP/2 Server Push 缓存) (最低优先级且特殊)

  • 触发条件: 服务器使用 HTTP/2 的 Server Push 功能主动推送资源给浏览器。

  • 机制:

    • 资源在服务器主动推送时被临时存储
    • 生命周期极短 :通常只存在于当前 HTTP/2 连接期间。关闭连接或打开新连接都会失效。
    • 同源限制宽松:即使跨域推送的资源也可能被存储(但遵循同源策略使用)。
    • 非常规获取方式: 资源必须是被 Server Push 推下来的,不能通过常规的 GET 请求去"读取" Push Cache。
    • 低优先级: 当浏览器需要某个资源时,如果它恰好在 Push Cache 中且有效(连接未关闭),可能会使用它。但它非常脆弱,通常作为优化手段(减少关键资源 RTT)。
  • 优先级原因: 存在性依赖特殊条件(HTTP/2 + Server Push),且生命周期短。如果其他缓存(Memory/HTTP)有有效副本,它们会被优先使用。Push Cache 是最后一道防线。

  • 面试要点: 说明其特殊性和临时性。知道它是 HTTP/2 的优化特性,但在实际缓存决策中作用有限且优先级最低。不是所有浏览器都支持或实现完善。

关键决策流程与优先级体现 (浏览器视角):

  1. 请求发起: 浏览器需要加载一个资源 (URL)。

  2. Service Worker 拦截?

    • 是: 执行 Service Worker 的 fetch 事件处理程序。

      • 如果处理程序返回了缓存响应 (无论来自 CacheStorage 还是其他地方):直接使用该响应 ,流程结束。 (最高优先级体现)
      • 如果处理程序决定忽略缓存并直接 fetch() 网络请求:则流程继续向下。
  3. Memory Cache 检查?

    • 浏览器检查当前内存中是否有该资源的有效副本。
    • 是且有效: 直接使用内存缓存 ,流程结束。 (次高优先级体现)
    • 否 或 无效: 流程继续。
  4. HTTP Cache (Disk Cache) 检查?

    • 浏览器检查硬盘上是否有该资源的缓存副本及其有效性。

    • 强缓存有效 (fresh): 直接使用磁盘缓存 ,不发任何请求,流程结束。 (核心缓存层)

    • 强缓存失效 (stale) 但有验证器 (ETag/Last-Modified):

      • 浏览器发起一个带验证头 (If-None-Match/If-Modified-Since)条件请求到服务器。
      • 服务器响应 304 Not Modified浏览器使用磁盘缓存中(已失效但未变)的副本 ,流程结束。 (协商缓存成功)
      • 服务器响应 200 OK (新内容):浏览器使用新响应,更新 HTTP 缓存,流程结束。
    • 无缓存 或 缓存无效且无验证器 或 no-store 流程继续。

  5. Push Cache 检查?(如果存在 HTTP/2 连接且之前有推送)

    • 浏览器检查当前连接关联的 Push Cache 中是否有该资源。
    • 是且连接有效: 可能 会使用它(但非常不可靠,优先级最低)。 (最低优先级体现)
    • 否 或 无效: 流程继续。
  6. 网络请求:

    • 以上所有缓存层均未提供有效响应。
    • 浏览器发起一个常规的 GET 请求到服务器。
    • 服务器返回响应 (200 OK)。
    • 浏览器根据响应头 (Cache-Control 等) 决定是否以及如何缓存这个新响应(存入 HTTP Cache / Memory Cache)。
    • 使用响应内容。

面试回答技巧与要点总结:

  1. 结构化阐述: 按优先级顺序清晰列出层级:Service Worker -> Memory Cache -> HTTP Cache (强缓存 -> 协商缓存) -> Push Cache -> Network。

  2. 强调关键点:

    • Service Worker 的绝对控制权:它最先拦截,可以决定一切。
    • Memory Cache 的速度优势与临时性
    • HTTP Cache 是核心战场 :深入解释 Cache-Control (max-age, no-cache, no-store)、强缓存(不发请求)与协商缓存(发条件请求,304 vs 200)的本质区别。这是面试官最可能深挖的地方。
    • Push Cache 的特殊性和低优先级
  3. 解释流程: 简述浏览器决策链:"浏览器会依次检查 Service Worker 是否拦截并返回缓存 -> 检查内存缓存是否有效 -> 检查 HTTP 强缓存是否有效 -> 如果强缓存失效则发条件请求检查协商缓存 -> 最后尝试 Push Cache -> 都失败才发网络请求"。

  4. 举例说明: 可以结合常见指令举例:

    • "Cache-Control: max-age=3600, public 表示资源可以被任何缓存(浏览器、CDN)存储,并且在 3600 秒内,浏览器会直接从内存或磁盘加载它,不发请求(强缓存)。"
    • "Cache-Control: no-cache 不表示不缓存,而是每次使用前必须 向服务器验证(发带 ETag/Last-Modified 的条件请求),如果服务器返回 304 则用缓存(协商缓存)。"
    • "Cache-Control: no-store 才是真正的不缓存,每次都必须从网络获取。"
  5. 提到启发式缓存: 如果响应没有明确指定 ExpiresCache-Control: max-age,但提供了 Last-Modified,浏览器可能 会根据 Last-Modified 和当前时间的差值(通常取该差值的 10% 作为 max-age)进行启发式缓存。这是一个常被忽略但重要的细节。

  6. 清晰区分状态码/来源:

    • 200 OK (from memory cache) / 200 OK (from disk cache):强缓存命中。
    • 304 Not Modified:协商缓存验证成功(服务器未修改)。
    • 200 OK (无特殊标注):来自网络请求。
  7. 结合实际优化: 最后可以提一下理解这些对性能优化的意义:如何设置合理的缓存策略减少请求、加快加载速度(静态资源长缓存+版本控制/指纹、API 谨慎缓存等)。

5、Service Worker 缓存是什么 如何实现

Service Worker 缓存是一种由 JavaScript 编程控制 的浏览器缓存机制,它是实现离线应用、后台同步、推送通知 以及高级缓存策略 的核心技术。它赋予了开发者对网络请求和响应的精细控制权,使其优先级凌驾于传统的 HTTP 缓存和内存缓存之上。

核心概念:

  1. 独立的 JavaScript Worker:

    • Service Worker 是一个运行在浏览器后台的独立 JavaScript 线程。
    • 它与主页面 JavaScript(运行在 Window 上下文)完全隔离,不能直接访问 DOM。
    • 通过 postMessage API 与页面进行通信。
  2. 代理网络请求:

    • Service Worker 的核心功能是充当位于浏览器和网络之间的代理。
    • 它可以拦截、修改、响应其作用域 (scope) 内的所有网络请求(包括页面导航请求、HTML、CSS、JS、图片、API 调用等)。
  3. 事件驱动:

    • 其生命周期和行为由一系列事件驱动:

      • install: Service Worker 首次安装或更新时触发。通常在此事件中进行资源预缓存
      • activate: Service Worker 被激活,开始控制其作用域内的页面时触发。通常在此事件中进行旧缓存清理
      • fetch: 每当作用域内的页面发起网络请求时触发。开发者可以在此事件中拦截请求并决定如何响应(从缓存返回、转发到网络、构造新响应等)。
      • message: 用于接收来自页面或其他 Service Worker 的消息。
      • sync / push: 用于后台同步和推送通知(需要额外配置)。
  4. 作用域 (scope):

    • 定义 Service Worker 可以控制哪些页面。默认是其脚本文件所在的目录及其子目录。
    • 注册时可以通过 scope 选项指定更宽或更窄的范围(必须在脚本文件所在路径下)。
  5. 缓存存储 (CacheStorage / caches):

    • Service Worker 使用 CacheStorage API(通过全局的 caches 对象访问)来存储和管理缓存。
    • 缓存存储在 Cache 对象中,每个 Cache 可以存储大量的 Request/Response 对。
    • 开发者可以创建多个命名缓存(如 v1-static-assets, v2-api-responses),便于管理和版本控制。
  6. 生命周期管理:

    • 安装 (Installing): 下载并解析 Service Worker 脚本。
    • 等待 (Waiting): 如果当前有活动的旧版 Service Worker 控制着页面,新安装的 Service Worker 会进入此状态,等待接管(除非调用 skipWaiting())。
    • 激活 (Activating): 新 Service Worker 获得控制权,触发 activate 事件。旧 Service Worker 及其控制的页面将被停用。
    • 活动 (Activated): 控制页面,处理 fetch 等事件。
    • 冗余 (Redundant): 被新版本替换或安装失败。

如何实现 Service Worker 缓存 (核心步骤):

  1. 注册 Service Worker:

    • 在主页面 JavaScript 中注册 Service Worker 脚本文件 (sw.js)。
    javascript 复制代码
    // main.js (或你的入口脚本)
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function() {
        navigator.serviceWorker.register('/sw.js', { scope: '/' }) // 注册 sw.js,作用域为根目录 '/'
          .then(function(registration) {
            console.log('ServiceWorker 注册成功,作用域: ', registration.scope);
          })
          .catch(function(error) {
            console.log('ServiceWorker 注册失败: ', error);
          });
      });
    }
  2. 编写 Service Worker 脚本 (sw.js) - 预缓存 (在 install 事件中):

    • install 事件中,打开一个缓存,并将需要预加载的关键静态资源(确保应用外壳能离线工作)添加到缓存中。
    • 使用 event.waitUntil() 确保安装过程直到缓存操作完成才结束。
    javascript 复制代码
    // sw.js
    const CACHE_NAME = 'my-site-cache-v1'; // 使用版本号便于后续更新
    const urlsToCache = [
      '/',
      '/index.html',
      '/styles/main.css',
      '/scripts/main.js',
      '/images/logo.png'
    ];
    ​
    self.addEventListener('install', function(event) {
      // 执行安装步骤:创建缓存并添加资源
      event.waitUntil(
        caches.open(CACHE_NAME)
          .then(function(cache) {
            console.log('已打开缓存');
            return cache.addAll(urlsToCache); // 缓存指定资源列表
          })
      );
    });
  3. 编写 Service Worker 脚本 (sw.js) - 清理旧缓存 (在 activate 事件中):

    • activate 事件中,清理掉不属于当前版本的旧缓存。
    • 使用 event.waitUntil() 确保清理操作完成。
    javascript 复制代码
    self.addEventListener('activate', function(event) {
      const cacheWhitelist = [CACHE_NAME]; // 保留当前版本缓存
    ​
      event.waitUntil(
        caches.keys().then(function(cacheNames) {
          return Promise.all(
            cacheNames.map(function(cacheName) {
              if (cacheWhitelist.indexOf(cacheName) === -1) { // 不在白名单中的缓存
                return caches.delete(cacheName); // 删除旧缓存
              }
            })
          );
        })
      );
    });
  4. 编写 Service Worker 脚本 (sw.js) - 拦截请求 & 响应 (在 fetch 事件中):

    • 这是实现缓存策略的核心!fetch 事件允许你拦截所有作用域内的请求。
    • 你可以实现各种策略来决定如何响应请求。以下是一些常见策略的代码示例:

    策略 1: 缓存优先 (Cache First - 适合静态资源)

    javascript 复制代码
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        caches.match(event.request) // 尝试在缓存中查找匹配的请求
          .then(function(response) {
            // 缓存命中,返回缓存响应
            if (response) {
              return response;
            }
            // 缓存未命中,发起网络请求
            return fetch(event.request)
              .then(function(networkResponse) {
                // 可选:将新获取的资源添加到缓存中 (动态缓存)
                // 注意:通常只缓存 GET 请求且成功的响应 (200)
                if (event.request.method === 'GET' && networkResponse.status === 200) {
                  const responseToCache = networkResponse.clone(); // 克隆响应(流只能读取一次)
                  caches.open(CACHE_NAME)
                    .then(function(cache) {
                      cache.put(event.request, responseToCache);
                    });
                }
                return networkResponse;
              });
          })
      );
    });

    策略 2: 网络优先 (Network First - 适合需要最新数据的请求)

    less 复制代码
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        fetch(event.request) // 优先尝试网络请求
          .then(function(networkResponse) {
            // 网络请求成功,返回响应并可选更新缓存
            if (event.request.method === 'GET' && networkResponse.status === 200) {
              const responseToCache = networkResponse.clone();
              caches.open(CACHE_NAME)
                .then(function(cache) {
                  cache.put(event.request, responseToCache);
                });
            }
            return networkResponse;
          })
          .catch(function(error) {
            // 网络请求失败,尝试从缓存中获取
            return caches.match(event.request);
          })
      );
    });

    策略 3: 仅缓存 (Cache Only) / 仅网络 (Network Only) / 更新后重新验证 (Stale-While-Revalidate)

    • 这些策略的实现逻辑类似,根据需求调整 caches.matchfetch 的顺序以及处理逻辑即可。
  5. 处理更新:

    • 当用户访问页面时,浏览器会自动检查 sw.js 是否有更新(字节比对)。

    • 如果有更新,会下载新脚本并触发新 Service Worker 的 install 事件(旧 Service Worker 仍在运行)。

    • 新 Service Worker 默认进入 waiting 状态,直到所有被旧 Service Worker 控制的页面都关闭(或用户强制刷新)。

    • 为了强制新 Service Worker 立即接管 ,可以在新 Service Worker 的 install 事件中调用 self.skipWaiting()

      lua 复制代码
      self.addEventListener('install', function(event) {
        self.skipWaiting(); // 强制新 SW 立即激活
        event.waitUntil(...);
      });
    • activate 事件中清理旧缓存(如前面所示)。

关键注意事项 & 最佳实践:

  1. HTTPS 或 localhost: Service Worker 只能在 HTTPS 网站或 localhost 上运行(开发环境),这是出于安全考虑。

  2. 作用域 (scope): 确保 scope 设置正确。Service Worker 只能控制其脚本文件所在路径及其子路径下的页面。

  3. 缓存策略选择: 根据资源类型选择合适的策略:

    • 静态资源(CSS, JS, 图片等): 通常使用 缓存优先 (Cache First) + 长 max-age HTTP 缓存 + 文件名哈希(内容指纹)实现"永久缓存"。Service Worker 确保即使 HTTP 缓存过期也能离线使用。
    • 需要频繁更新的数据(API 响应、用户内容): 通常使用 网络优先 (Network First)更新后重新验证 (Stale-While-Revalidate) ,优先获取最新数据,同时提供缓存作为后备或加速。
    • 关键页面(HTML): 策略需谨慎。网络优先 + 缓存后备能保证内容更新,但也可能造成离线不可用。缓存优先能保证离线可用,但可能展示旧内容。通常结合应用外壳 (App Shell) 模型。
  4. 缓存管理:

    • 版本控制: 每次更新静态资源或 Service Worker 逻辑时,务必更新缓存名称 (CACHE_NAME) ,并在 activate 事件中清理旧缓存。
    • 限制缓存大小: Service Worker 缓存没有自动大小限制(不同于 HTTP 缓存)。需要自行实现逻辑(如 caches.keys() + cache.entries() 计算大小,删除最旧/最少使用的缓存项)。
    • 避免缓存过多: 只缓存必要的资源。预缓存关键资源,动态缓存按需添加。
  5. 调试:

    • Chrome DevTools -> Application -> Service Workers: 查看注册状态、调试代码、强制更新、模拟离线状态、检查 CacheStorage。
    • Chrome DevTools -> Network: 查看请求是否被 Service Worker 拦截(Size 列显示 (ServiceWorker)),状态码为 200 (from ServiceWorker)
  6. 用户体验:

    • 离线页面:fetch 事件中,如果网络和缓存都失败,可以返回一个预缓存的离线页面 (offline.html)。
    • 更新提示: 监听 registration 对象的 updatefound 事件和 installing worker 的状态变化,通知用户有更新可用,并引导刷新页面以激活新 Service Worker。

总结:

实现 Service Worker 缓存的核心步骤是:注册 -> 安装时预缓存关键资源 -> 激活时清理旧缓存 -> 在 fetch 事件中实现缓存策略拦截请求并返回响应 。它提供了无与伦比的灵活性和控制力,是构建快速、可靠、离线可用 的现代 Web 应用(PWA)的基石。在面试中,清晰地阐述其核心概念、生命周期、事件驱动模型(特别是 install, activate, fetch)以及如何实现不同的缓存策略,能充分展示你对高级浏览器缓存机制的理解。

6、浏览器是如何决定哪些资源缓存到本地哪些资源缓存到内存呢

浏览器决定将资源缓存到磁盘(Disk Cache / HTTP Cache) 还是内存(Memory Cache) 是一个复杂的动态决策过程,涉及多种因素。开发者无法直接控制,但理解其逻辑有助于优化缓存策略。以下是关键决策因素:

一、核心决策因素

1. 资源类型与大小
存储位置 典型资源类型 原因
内存缓存 - 小体积资源(JS、CSS、小图标) - 当前页面高频访问的资源(如首屏图片、核心JS) 内存读取速度极快,适合快速响应当前页面的重复请求。 内存空间有限,优先存高频小资源。
磁盘缓存 - 大体积资源(大图、视频、字体文件) - 低频访问的静态资源 - 强缓存策略明确的资源(max-age较长) 磁盘空间大(GB级),适合存大文件。 持久化存储,可跨会话使用。
2. 缓存策略(HTTP头部)
  • Cache-Control 优先级最高

    • 若资源标记为 no-store不缓存
    • 若为 no-cache缓存但每次需验证(可能存内存或磁盘)。
    • 若为 max-age=3600按策略缓存(磁盘优先,大资源存磁盘)。
  • 启发式缓存 :无明确过期时间时,浏览器根据 Last-Modified 时间推算有效期(如 (Date - Last-Modified) * 0.1),通常存磁盘。

3. 访问频率与时效性
行为 存储倾向 案例
同一页面内多次请求 内存缓存 首页加载后,脚本二次请求(如异步加载模块)直接从内存读取。
跨页面/会话重复请求 磁盘缓存 站点LOGO图片在多页面间共享,从磁盘读取。
短时效资源 内存缓存 实时性较高的API响应(如股票数据),短暂存内存。
4. 浏览器内部算法
  • LRU(Least Recently Used)算法

    • 内存缓存 :空间紧张时优先淘汰最久未使用的小资源。
    • 磁盘缓存:自动清理过期资源,按访问频率保留常用资源。
  • 分区限制

    • 内存缓存:单进程/Tab独立(Chrome),关闭Tab即释放。
    • 磁盘缓存:全局共享,受操作系统磁盘空间限制。

二、开发者视角的优化建议

1. 利用HTTP头部引导缓存
ini 复制代码
# 引导存磁盘(大文件/长期缓存)
Cache-Control: public, max-age=31536000, immutable
​
# 引导存内存(小文件/高频资源)
Cache-Control: max-age=600, must-revalidate

:浏览器最终决策权仍在其内部算法。

2. 资源分割策略
  • 高频小资源 :合并为单文件(如Webpack打包的vendor.js)→ 易被内存缓存。
  • 低频大资源:单独拆分(如视频、字体)→ 避免挤占内存缓存。
3. 避免破坏缓存的行为
  • 随机查询参数image.png?v=123 会被视为新资源,导致缓存失效。
  • 动态内容无缓存头 :API响应未设Cache-Control → 不缓存或短时内存缓存。

三、缓存位置验证(开发者工具)

Chrome DevTools 中查看:

  1. Network面板 → 查看资源请求的 Size 列:

    • (memory cache)内存缓存
    • (disk cache)磁盘缓存
  2. Application面板

    • Cache Storage:查看Service Worker缓存。
    • Cache:查看HTTP磁盘缓存内容(需手动刷新)。

四、特殊场景处理

场景 缓存行为
无痕模式 内存缓存可用,磁盘缓存禁用(关闭标签后清除所有缓存)。
页面刷新(硬刷新) 跳过内存缓存,强制检查磁盘缓存和网络(Cache-Control: max-age仍有效)。
Service Worker拦截 优先级最高,可覆盖内存/磁盘缓存(如fetch事件返回自定义响应)。

总结:浏览器缓存决策逻辑

核心原则 : 浏览器以性能最优为目标,综合资源类型、大小、访问频率、HTTP策略动态分配存储位置。开发者应通过合理的缓存头部和资源优化策略,间接引导浏览器缓存行为。

相关推荐
Henrii_历小海4 分钟前
WhatsApp Web 不是手机镜像:多设备协议、本地数据流与出海获客流水线
前端·智能手机
ayqy贾杰11 分钟前
Claude Fable 5 提示词泄漏,抓紧学习下
前端·后端·面试
浩瀚地学28 分钟前
【面试算法笔记】0106-数组-区间和
笔记·算法·面试
To_OC8 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic9 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy9 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志9 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble10 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯11 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜12 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js