多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

前端缓存优化实战:从HTTP缓存到Service Worker的完整指南

前端缓存优化实战:从HTTP缓存到Service Worker的完整指南 做前端这些年我一直有个很深的体会很多团队做性能优化上来就是压图片、拆包、上CDN一顿操作猛如虎回头一看页面里那些本该被浏览器“记住”的静态资源每次刷新还在从服务器重新拉一遍。辛辛苦苦把体积从2MB降到600KB结果用户第二次访问还是等了好几百毫秒的网络请求。这背后往往就是同一个问题前端缓存没做好。缓存这个东西听起来谁都知道但真正在项目里把它配置得明明白白的团队说实话不多。它不像代码分割、懒加载那样“看得见摸得着”改完出了一个新包就有成就感缓存配置完你很难在当次发布里看到肉眼可见的变化所以它特别容易被低估。但这篇文章想告诉你的是前端缓存可能是整个性能优化体系里性价比最高的一环它解决的不是“首次访问能多快”而是“大多数访问能有多快”。不管你是刚入行的前端新人还是带团队做技术方案的老手把缓存体系理清楚都能让线上表现上一个台阶。1. 前端缓存到底在解决什么问题1.1 一次页面加载背后有多少重复请求先做个简单的实验。你打开一个典型的电商SPA页面Network面板里通常会有几十上百个请求HTML文档、入口JS、路由分包、公共依赖、一堆CSS、字体文件、商品图片、用户信息接口、推荐位接口……这些资源里真正的“增量”只有用户每次新产生的数据而绝大部分静态资源两个小时内打开十次页面内容都一模一样。但如果没有缓存策略每一次刷新浏览器都得重新跟服务器握手重新下载同样的字节流。我见过最夸张的情况是一个企业后台管理项目登录后每次跳转页面公共依赖的vendor.js都要重新加载一次一坨没有改过的老代码白白消耗几百毫秒还在弱网环境里经常把用户卡在白屏页。所以缓存解决的核心问题不是“省流量”而是压缩用户感知上的加载链路。我们把一次页面加载拆开看DNS解析、TCP建连、TLS握手、发送请求、等待响应、下载内容、解析执行。缓存能干掉的是“发送请求”“等待响应”“下载内容”这几大块。对于静态资源来说这几块往往占了整个加载时间的大头。你辛辛苦苦把JS从200KB压缩到120KB缓存只要命中一次这120KB的下载时间直接变成0区别就是这么直接。1.2 缓存不是一个点而是一条链很多人一提缓存想到的就是给静态资源加个Cache-Control头然后就完事了。但真实的缓存体系是分层的每一层解决的场景不同。我习惯把它拆成四个层次HTTP协议层的缓存、浏览器内存/磁盘缓存、Service Worker可控缓存、CDN边缘缓存。HTTP缓存是最基础的一层靠响应头告诉浏览器这个资源可以缓存多久浏览器缓存是HTTP缓存的落地执行者它决定资源到底放在内存里还是落到磁盘上Service Worker是开发者手里的“自定义缓存层”它能绕过HTTP缓存的默认行为按自己的策略决定从哪取资源CDN缓存则把静态资源推到离用户更近的边缘节点缩短物理距离带来的延迟。这四层不是互相替代的关系而是从“客户端本地”到“边缘网络”的层层递进。理想的命中顺序是Service Worker先拦截命中就直接返回没命中再看内存缓存和磁盘缓存还没有才发请求到CDNCDN再回源到服务器。每一层往前递进网络开销都成倍增加。你在优化时得知道当前项目卡在哪一层而不是盲目给所有资源都套一套最长缓存。1.3 为什么它经常被低估可能有人会说缓存这个东西有什么好讲的浏览器不是天生就会缓存吗。这话对了一半。浏览器确实默认会缓存一部分资源但默认行为和“合理配置”之间差得很远。默认情况下很多服务器不会主动返回足够的缓存控制字段尤其是CDN和源站没有统一配置时响应头可能连Cache-Control都没有浏览器只能靠启发式算法猜一个缓存时间这种不可控的“猜”往往会带来两个极端要么缓存时间过短静态资源每次还在重复请求要么缓存了明明不该缓存的动态接口导致用户看到旧数据。另外缓存优化不像压缩图片、合并请求那样改完能立刻在性能报告里看到“单项得分提升”它影响的是整体加载体验需要结合Lighthouse性能分数、真实用户监测数据、服务器访问日志来分析。正因为它“看不见摸不着”所以在很多项目的优化优先级列表里被排在了比较靠后的位置。但恰恰是这种不被重视让它成了一个低成本、高回报的切入点。一个简单的Cache-Control配置可能比你在webpack里折腾半天分包配置带来的收益更大。2. HTTP缓存原理与配置细节2.1 强缓存Cache-Control到底怎么写HTTP缓存最核心的机制是强缓存。所谓“强缓存”就是浏览器判断本地缓存还没过期直接使用本地副本连请求都不会发到服务器。这个“判断”完全依赖响应头里的Cache-Control字段。在实际配置里有一段配置我用得最多也建议所有团队先把这个搞定location /static/ { expires 30d; add_header Cache-Control public, max-age2592000, immutable; }这一行的意思是/static/目录下的静态资源允许任何节点缓存有效期30天并且immutable告诉浏览器这个资源在这30天内一定不会变刷新页面时也不需要重新验证。max-age单位是秒计算方式是30天乘以24小时乘以3600秒。这里的immutable是一个很实用但容易被忽略的属性它专门解决“用户刷新页面导致强缓存失效”的问题。没有immutable时用户按一下刷新键浏览器会认为资源需要重新验证于是发请求去问服务器“资源变没变”这违背了我们希望“完全不发请求”的初衷。不同的资源类型Cache-Control配置思路完全不一样我整理了一张表供参考资源类型推荐配置说明HTML入口文件no-cache或max-age0每次必须回源验证带hash指纹的JS/CSSpublic, max-age31536000, immutable文件名一变就相当于新资源字体文件public, max-age31536000, immutable字体很少变长期缓存图片public, max-age2592000一个月或一年均可接口数据GET按业务max-age60或no-cache只在幂等接口上启用注意HTML文件千万别设max-age31536000因为HTML是应用入口页面引用的JS和CSS都靠它来“指路”。如果HTML被浏览器长期缓存你更新了静态资源文件名用户浏览器还是拿着旧的HTML去加载旧的JS新版本根本推不下去。2.2 协商缓存304它不是黑魔法当资源超过max-age有效期或者响应头设置了no-cache时浏览器并不会直接放弃缓存而是进入“协商缓存”阶段带着缓存的标识去问服务器“我手里这个版本还能用吗”。服务器说能用就返回304状态码不返回资源体服务器说不能用就返回200带上新资源。协商缓存靠两个头部Last-Modified和ETag。Last-Modified是服务器告诉浏览器资源最后修改时间浏览器下一次带着If-Modified-Since字段去验证ETag是资源的唯一标识通常是文件内容的哈希浏览器下一次带着If-None-Match字段去验证。ETag的优先级高于Last-Modified因为文件修改时间有精度问题内容相同但时间变了的情况很容易误判。实际开发中我建议默认配置以ETag为主。Nginx默认就会生成ETag和Last-Modified如果你用的服务器是自己写的Node服务可以用etag这个npm包来生成或者直接用内容哈希拼一个。这里贴一个Express中间件风格的简化示例const generateETag (content) { return require(crypto).createHash(md5).update(content).digest(hex); }; // 响应时设置 ETag res.setHeader(ETag, ${generateETag(content)});注意ETag的值要用引号包起来这是HTTP规范要求的格式浏览器端不认裸哈希。2.3 缓存配置里的三个常见坑第一个坑是“信任默认值”。我见过不少项目的Nginx配置里完全没有缓存相关的指令静态资源请求返回的响应头只有Connection: keep-alive没有Cache-Control也没有Expires。浏览器这时候会按启发式算法用Last-Modified和当前时间差的一部分作为缓存时间通常不短但也不可控。更麻烦的是有的JS接口服务设了Cache-Control: no-store误伤了一些本该缓存的静态资源导致每次刷新都全量回源。第二个坑是“一刀切”。有些团队图省事给所有响应统一设置Cache-Control: no-cache。这会带来一个结果每次页面加载所有静态资源都要发至少一次请求做“是否变更”的验证304返回虽然体积小但网络往返次数并没有省下来。真正完美的配置是“末尾处最长缓存”HTML走协商缓存带hash的资源走强缓存一年接口按数据特性单独处理。第三个坑和CDN相关。你在源站配置了Cache-Control: public, max-age3600但CDN厂商可能默认不缓存带Cookie的请求或者回源时会改写响应头。我曾经排查过一个诡异的问题源站明明返回了max-age86400用户端的响应头却变成了max-age300最后发现是CDN平台侧配置了一个兜底缓存策略覆盖了源站头部。所以配缓存时源站、CDN、浏览器三层要一起对一遍别只盯一层。3. 浏览器内存缓存与磁盘缓存的微妙差异3.1 Memory Cache和Disk Cache的区别打开DevTools的Network面板刷新几次页面你会在某些请求的Size列看到一行小字from memory cache或者from disk cache。这就是浏览器内部的两级缓存内存缓存和磁盘缓存。内存缓存的读取速度极快基本在几微秒级别但它有一个致命弱点——生命周期很短。浏览器进程崩溃、页面关闭、内存紧张被回收都会导致内存缓存失效。磁盘缓存的读取速度相对慢一些但持久性好数据存在硬盘上下次打开浏览器还在。浏览器决定一个资源进哪级缓存算法非常复杂但有几个规律是稳定的体积小、加载频率高的资源更倾向进内存比如脚本、CSS、字体体积大、可能很久才再次访问的资源更倾向落磁盘比如图片、视频。DevTools的Network面板里from memory cache通常出现在刷新页面时而from disk cache在冷启动、重新打开浏览器标签时更常见。这些差异我们无法直接干预但理解它能帮你判断缓存是不是真的生效了。3.2 什么资源会命中哪种缓存我在实战中观察到几个现象分享出来给大家参考。第一页面里通过link relpreload预加载的字体文件几乎总是进内存缓存因为浏览器明确知道这个资源马上会被使用。第二图片在弱网设备上更容易落磁盘因为内存不够用了。第三同样是刷新正常按F5和CtrlShiftR强制刷新命中情况完全不同。强制刷新会绕过强缓存直接走协商缓存很多不常调试的人看到from disk cache不见了会误以为缓存有问题其实只是刷新方式不同。还有一个容易忽略的细节如果服务器返回了Cache-Control: no-store浏览器不会缓存Network面板里资源每次都是真实网络请求。而如果返回的是privateCDN不能缓存但浏览器可以缓存这就回答了“为什么我用无痕模式测缓存老是不生效”的问题——无痕模式本身可能直接禁用磁盘缓存所有资源都走内存。3.3 实测记录命中缓存后到底快多少说一个具体数据。我之前给一个中后台项目做优化页面里公共vendor包大概1.1MB首次加载在办公室网络下耗时约430ms。配置好强缓存后第二次刷新这个资源从memory cache直接读取耗时降到0msNetwork面板里连请求都不显示耗时条。整个页面的加载时间从2.1秒降到了700ms左右而这次优化动的东西只有Nginx配置和打包输出的hash命名。这时候就会有人问那是不是意味着只要命中缓存就万事大吉了也不全是。缓存命中的前提是资源确实是“不变的”。如果你的构建工具没有给文件名打上可靠的hash指纹旧缓存会让线上更新变成一场灾难。所以缓存和构建配置是配套的hash指纹的正确性直接影响缓存策略的安全性。这也是下一部分要聊的重点如何把缓存主动权抓在自己手里。4. Service Worker把缓存主动权抓在手里4.1 Service Worker能做什么HTTP缓存的执行者是浏览器开发者只能通过响应头“间接控制”。但有些场景我们希望“直接控制”比如已经加载过的页面希望离线也能访问某些接口希望先返回缓存数据再在后台悄悄更新某些第三方的静态资源想用本地副本替代线上。这些需求HTTP缓存做不到但Service Worker可以。它是浏览器提供的一个独立于页面脚本的代理层可以拦截页面发出去的所有请求做自定义调度。核心生态是caches这个API配合三个生命周期事件install、activate、fetch。Service Worker能做的缓存策略远不止“过期时间”这一种维度它可以从业务角度来决定什么时候预取、什么时候用缓存兜底、什么时候强制走网络、什么时候更新缓存。这意味着缓存从“浏览器自动档”切换为“开发者手动档”。下面是最基础的一段注册代码写在页面主入口里if (serviceWorker in navigator) { window.addEventListener(load, () { navigator.serviceWorker.register(/sw.js).then(registration { console.log(SW registered, registration.scope); }).catch(err { console.log(SW registration failed, err); }); }); }4.2 三种常用策略Cache First、Network First、Stale-While-RevalidateService Worker的缓存策略说起来很灵活但核心是三种玩法理解了这三种剩下都是排列组合。第一种叫Cache First就是有缓存直接用缓存绝不去网络。这种策略最适合那些不常变、但体积较大的静态资源。代码大概是这样的self.addEventListener(fetch, event { if (event.request.url.includes(/static/)) { event.respondWith( caches.match(event.request).then(cached { if (cached) { return cached; } return fetch(event.request).then(response { const clone response.clone(); caches.open(v1).then(cache cache.put(event.request, clone)); return response; }); }) ); } });第二种叫Network First优先走网络网络失败才用缓存。这种适合接口请求尤其是GET类的业务接口能保证数据尽量新同时弱网时页面不至于直接崩溃。event.respondWith( fetch(event.request) .then(response { const clone response.clone(); caches.open(dynamic).then(cache cache.put(event.request, clone)); return response; }) .catch(() caches.match(event.request)) );第三种叫Stale-While-Revalidate最为灵活先马上返回缓存内容让用户第一时间看到东西同时在后台发起网络请求更新缓存。等下次用户再访问时用的就是新数据了。典型场景是新闻列表、头像信息这类“可以容忍略微陈旧”的数据。event.respondWith( caches.match(event.request).then(cached { const fetchPromise fetch(event.request).then(response { const clone response.clone(); caches.open(revalidate).then(cache cache.put(event.request, clone)); return response; }); return cached || fetchPromise; }) );这三种策略有一个共同的注意点缓存写入时不能直接用原始的response对象必须用response.clone()。因为response是一个流只能被消费一次读完就没了。4.3 Service Worker与HTTP缓存的协作很多人以为上线了Service WorkerHTTP缓存就没用了这是误解。两者是前后端配合的关系。Service Worker本身也可能被HTTP缓存影响它默认的更新检查频率很低每次用户访问都会后台检查一次新版本但更新太频繁反而会影响性能。在实际项目中我会把HTTP缓存当作“第一道防线”Service Worker当作“第二道可控防线”。对于静态资源HTTP强缓存已经能解决绝大多数场景Service Worker负责的是离线访问和接口的容错降级。而不太建议把所有资源都交给Service Worker缓存因为它的维护成本远高于HTTP缓存需要处理版本兼容、清理旧缓存、等待激活时机等问题。有一点特别值得注意Service Worker里更新缓存的时机。线上发布新版本后如果用户在旧页面停留Service Worker后台已经拉到了新资源但页面还是旧代码在跑这时候必须配合skipWaiting和clients.claim来接管旧页面同时清理旧版本的缓存。有一段类似下面的代码我每个我的SW脚本里都会保留self.addEventListener(activate, event { event.waitUntil( caches.keys().then(keys Promise.all( keys.filter(key key ! v2).map(key caches.delete(key)) )).then(() self.clients.claim()) ); });5. 缓存策略组合给真实项目配一套方案5.1 不同资源的最优缓存方案把前面讲的东西串起来一个完整的缓存方案一定是按“资源类型业务特性”分层的而不是一套规则走天下。我给自己负责的项目定的标配是这样的。HTML入口文件使用Cache-Control: no-cache也就是禁用强缓存但允许协商缓存。这样每次访问都会带ETag回源验证内容没变就返回304内容变了立刻拿到最新页面。配合CDN时要注意Set-Cookie响应头会阻止CDN缓存HTML不过对入口文件来说这不是坏事。带hash的静态资源比如app-8f3d9c.js这种构建产物文件名里已经有内容的哈希一旦内容变化文件名就变了。这类资源可以大胆设置Cache-Control: public, max-age31536000, immutable。哪怕缓存一年用户重新请求时拿到的都是“新的文件名”不存在旧资源问题。这里有个细节构建工具生成hash时建议根据文件内容生成而不是根据文件修改时间否则文件名变了内容没变白白让用户重新下载。图片类资源按是否需要经常替换来决定。商品主图建议max-age86400允许一天的缓存运营更新图片后第二天能看到新图Logo、品牌图标这种很少变的一年缓存也没问题。字体文件一般一年图标字体最好配合preload让浏览器尽早请求并缓存。接口数据需要按幂等性判断。GET请求且没有副作用的比如商品详情、用户信息可以设置Cache-Control: private, max-age60让浏览器在一分钟内不重复请求如果接口带登录态或涉及敏感信息用private避免CDN缓存如果接口有副作用POST、PUT这类直接no-store。5.2 一个完整的发布与缓存更新流程很多团队有疑问静态资源一年缓存那我发新版时用户什么时候才能看到变化答案很简单靠hash指纹让浏览器把新旧资源当成两个完全不同的文件。用户第一次访问新页面时HTML文件走协商缓存拿到了新的index.html里面引用的app-新hash.js是浏览器没见过的URL于是发起新请求下载新代码而vendor-新hash.js也是如此。旧的app-老hash.js已经在用户本地缓存里再也没有页面引用它慢慢就成了垃圾。这套机制的难点不在“发布”而在“回滚”。假设你这次发版引入了一个严重Bug需要紧急回滚到上一个版本。回滚时构建出的文件名hash又变了用户拿到的是新的HTML和新的静态资源但数据模型却不兼容这时候你会发现用户的浏览器还缓存着“出问题的那个版本”可能要等很久才会去重新请求。这个问题的解药不是让静态资源不缓存而是在发布系统里做灰度发布和快速恢复前端缓存本身是无辜的。不过有一个操作可以改善回滚体验打包时把公共依赖单独提取成vendor包并且hash稳定不变。这样业务代码回滚了公共库里的小幅变化不会引起vendor文件名的变化用户能复用已缓存的公共依赖回滚时只需要重新拉业务代码本身。既然说到hash就要顺便提一句文件名必须是“内容hash”不是“时间戳”。如果你用时间戳做版本号会导致每次构建文件URL都变等于把所有缓存都推翻了性能优化就白做了。5.3 用性能指标客观评价缓存收益技术方案做得好不好不能靠感觉要看数据。缓存优化的收益主要体现在三个指标上LCP最大内容绘制、TTFB首字节时间、以及“重复访问时的加载时间下降比率”。LCP前端优化最常见的手段就是缓存让首屏的Hero图片能长时间缓存避免重复下载让入口JS能快速命中缓存缩短从HTML加载到脚本执行的间隔。Lighthouse跑分时如果“首屏可交互时间”一项分数低先别急着怪代码体积去Network面板看看首屏请求列表里有多少个资源是从服务器重新拉取的。很多情况下强制缓存一配这个指标直接就从红色跳到绿色。TTFB主要受网络链路影响缓存对TTFB本身没有直接作用但命中缓存时网络请求为0TTFB这一项在性能分析里就“不存在了”整体加载时间就会显著下降。我在真实项目中统计过一次优化前后的数据对比同样一批用户同样的网络环境首页加载时间中位数从3.2秒降到了1.1秒主要就是靠强缓存和预加载设置。另外如果你接入了真实用户监测系统RUM可以单独看“二次回访用户”的指标这是判断缓存效果的核心群体。如果二次回访加载时间和首次访问差不多那你的缓存策略大概率是白配了。6. 常见问题与排查技巧实录6.1 改了代码线上还是旧版本这是缓存问题里最高的投诉榜首。前端改了一版界面发布到服务器自己浏览器CtrlShiftR强制刷新看到新页面但用户反馈还是旧版本。排查思路分三路展开。第一路先看服务器响应头打开DevTools找到HTML请求看响应头里的Cache-Control。如果HTML的Cache-Control是public, max-age3600之类的那么你的HTML已经被强缓存了用户拿到的入口页面就是旧的后面引用的一切资源都跟着旧。正确做法是HTML的Cache-Control改为no-cache允许协商缓存而不是强缓存。第二路看CDN有没有缓存HTML很多CDN默认把HTML也缓存就算源站返回no-cacheCDN节点的边缘缓存还是会拦截一部分请求导致一部分用户拿到旧版本。这种情况需要去CDN控制台单独配置HTML文件的缓存策略。第三路查是不是Service Worker在捣乱如果项目注册了Service Worker并且用的是Cache First策略那无论服务器怎么改浏览器都是从本地缓存读取看起来好像怎么也更新不了。需要在发布后主动触发Service Worker更新更新缓存版本号。6.2 请求返回200而不是304到底正不正常很多同学对304有执念觉得请求只要不是304就没走缓存。这是个常见的误区。304只代表“协商缓存生效”意味着你发了一次网络请求服务器回应“内容没变别下载了”。而强缓存命中时请求根本不会发出去Network面板里显示的是from disk cache或from memory cache状态码依然是200。所以看到200并不代表缓存失败要看Size列。如果静态资源显示的是真实的网络耗时不是从缓存里取出来的那就要去看它为什么没有进缓存。可能原因是响应头的Cache-Control是private而你在浏览器无痕模式下测试也可能是服务器网关在中间篡改了缓存头。排查时点开请求详情看Response Headers里到底有没有Cache-Control和Expires没有就说明源站配置没有生效。还有一种情况是资源请求带上了Authorization头CDN会把这类请求当作私有请求默认不缓存。如果你的项目登录态是通过Authorization头传递的CDN缓存就需要单独配置“忽略Authorization头”的规则否则静态资源也会被疯狂回源。6.3 实战排查案例一次后台项目的“假缓存”问题之前给一个后台系统做优化页面里有一个报表组件每次进入都要等将近1秒才能显示。开发排查代码感觉是接口性能问题但接口实际响应只有80ms。后来我打开Network面板发现报表所需的那个JSON配置文件每次加载都是真实的网络请求耗时620ms。点开响应头源站是Java服务返回的响应头写了Content-Type: application/json但是没有Cache-Control。这个JSON文件内容其实一个月的都不变当时我给生产环境单独加了一个反向代理层对/report/config.json这个路径额外设置了缓存头Cache-Control: public, max-age86400。改完之后报表组件从670ms降到了20ms问题根本不是接口性能就是缓存策略没覆盖到这个静态配置文件。这个案例给我的启发是缓存问题的排查一定要结合“业务数据特性”来判断而不是单纯看技术指标。凡是“多读少变”的资源都应该优先考虑加缓存凡是“频繁更新”的资源哪怕再小也不要贸然设强缓存。6.4 几个顺手的调试工具和技巧Chrome DevTools的Network面板是缓存调试的主战场有几个功能值得认真用。Network面板左上角的Disable cache勾选框适合开发时强制不走缓存但它只对当前打开的DevTools生效不影响用户浏览器真实行为。想模拟用户首次访问用无痕窗口更靠谱。右键点击请求列表的任意位置选择Clear browser cache可以清掉浏览器层面缓存选择Clear site data会连带清掉本地存储、Cookie、Service Worker等一切站点数据。改缓存配置后想彻底验证优先用这两个。Application面板里的Cache Storage能直接看到Service Worker写入的缓存内容排查SW问题基本靠它。另外DevTools的Network面板如果添加Cache-Control这一列你能一次看到所有请求的缓存状态。6.5 缓存优化的一个根本性检查清单给团队分享了我多年的缓存排查经验后我总结出一份检查清单新接手一个项目时我一般会按这个顺序查一遍[ ] HTML响应头是否有Cache-Control: no-cache是否允许协商缓存。[ ] 静态资源文件名是否带内容hash不带hash的必须改。[ ] 静态资源响应头是否设置max-age是否允许CDN缓存。[ ] CDN平台侧是否对HTML单独设置了不缓存规则。[ ] 接口中哪些是幂等的GET请求是否分类配置了缓存头。[ ] Service Worker是否存在如果存在版本更新机制是否在所有页面生效。[ ] 无痕模式与正常模式下Network面板的Size列表现是否一致。[ ] 用Lighthouse或真实用户监测数据看二次访问时的加载时间是否明显降低。这份清单看着长但每一项都踩过坑。逐个检查完基本上整个项目的缓存策略就清晰了。最后的几点个人体会做性能优化这些年我的最大体会是缓存不是一项“锦上添花”的功夫它是性能优化的地基。地基歪了上面做再多的压缩、分包、懒加载都事倍功半。你费劲把包体积压缩了20%缓存策略一塌糊涂用户每次刷新都在重新下载那20%的优化根本不重要。反过来只要把缓存配置到位哪怕代码体积不算最优用户的重复访问体验都能维持在一个很舒服的水平上。我甚至建议把“缓存评审”放进每一次Code Review的必备项新增接口时多问一句“这个接口能被缓存吗”新增静态资源时多看一眼构建产物有没有带hash。这些细节看起来微不足道但对线上体验的影响非常大。最后再分享一个小技巧如果你在配置缓存时拿不准某个资源该设多久我的默认答案永远是“宁可偏向长缓存也要把hash指纹做好”。只要hash可靠长缓存带来的风险远小于它带来的收益。缓存这件事值得每个前端团队认真对待。
返回列表