从零构建单级交流放大电路:核心原理与设计实践【电子技术】
2026/5/14 18:15:05
从“甩锅”到“兜底”,一套代码实现缓存自愈,把用户体验拉回 100 分
“老师,页面白屏了!”
“清下浏览器缓存试试。”
—— 这段对话每天都在各家公司重复上演。
用户不会理解「缓存」是什么,他们只会觉得“你们网站又出 Bug 了”。
更尴尬的是,90% 的线上“旧代码”问题,确实只靠强制刷新就能解决。
于是前端背锅,用户流失,产品经理发飙。
让用户永远不再看到旧代码,同时永远不再听到“清缓存”三个字。
| 缓存位置 | 谁控制 | 典型场景 | 是否可 JS 感知 |
|---|---|---|---|
| Memory Cache | 浏览器 | 同一会话后退/刷新 | ❌ |
| Disk Cache (HTTP 缓存) | Response Header | 强缓存 200(from disk) | ❌ |
| Service Worker Cache | 开发者代码 | PWA 离线包 | ✅ |
| Push Cache | HTTP/2 | 已废弃 | ❌ |
只有 Service Worker 能让前端“自己管自己”,其余都无法在出错时主动清理。
因此「让用户清缓存」本质是把不可控因素甩给用户——极不专业。
__APP_VERSION__ = '1.2.3-beta.1+202509211100'?v=latest的 5% 流量走新版本,出错自动回滚# .github/workflows/release.ymlecho"export const APP_VERSION = '${GITHUB_REF_NAME}+$(date+%Y%m%d%H%M)';">src/meta/version.js// vite.config.tsimport{defineConfig}from'vite'import{APP_VERSION}from'./src/meta/version'exportdefaultdefineConfig({define:{__APP_VERSION__:JSON.stringify(APP_VERSION),},})constVERSION_CHECK_INTERVAL=60_000// 1minconstRETRY_MAX=3asyncfunctionfetchMeta(){// 加 search 防止自身被缓存constres=awaitfetch('/meta.json?t='+Date.now())returnres.json()asPromise<{version:string}>}exportfunctionstartVersionGuard(){letretry=0constloop=async()=>{try{const{version}=awaitfetchMeta()if(version!==__APP_VERSION__){// 发现新版本constevent=newCustomEvent('sw-update',{detail:{version}})window.dispatchEvent(event)// 立即刷新,skipWaiting 效果location.reload()}else{retry=0}}catch(e){if(++retry>=RETRY_MAX&&import.meta.env.PROD){// 可能 index.html 都是旧的,强制硬刷新location.href=location.href+'?v='+Date.now()}}}setInterval(loop,VERSION_CHECK_INTERVAL)loop()// 立即执行一次}window.addEventListener('error',(e)=>{constsrc=e.filename??''if(/chunk-.*\.js$/.test(src)&&e.message.includes('Failed to fetch')){// 旧 chunk 404sessionStorage.setItem('force-reflow','1')location.href=location.href+'?v='+Date.now()}})location = /index.html { add_header Cache-Control "no-cache, no-store, must-revalidate"; }// vite 默认 chunk-[hash].js,确保文件名一变就 404,触发兜底build.rollupOptions.output.entryFileNames='static/js/[name]-[hash].js'if ($arg_v = latest) { proxy_pass http://new-bucket; }/meta.json 只有 200B,1 分钟一次,1 万日活一天才 1k×60×24 ≈ 1.4M 请求,静态文件 CDN 0.01 元/万次,成本忽略不计。
监听 visibilitychange,切回前台立即检查版本;再配合 Service Worker 的 clients.claim() 可瞬间激活新代码。
内网场景建议把 index.html 做成 no-cache,发版通知用户刷新当前页即可;其余资源仍走哈希缓存,平衡速度与可靠性。
“清缓存”本质是把技术债转嫁给用户。
只要做到:
就能让发版像“热更新”一样丝滑。
下次再有人让你“清缓存”,请把这篇博客甩给他,并温柔地说:
“不用,我们网站自己会洗澡。”