聊聊web前端的缓存问题

1. 浏览器缓存机制

强缓存

  • 相关HTTP头:Cache-Control (优先级高)和Expires
  • 常见指令:max-age (缓存时间)、no-cache (跳过强缓存)、no-store(完全禁用缓存)
  • 示例:Cache-Control: max-age=3600 表示资源缓存1小时

协商缓存

  • 相关HTTP头:ETag(文件哈希)和 Last-Modified(最后修改时间)
  • 流程:浏览器发送请求时携带 If-None-Match(对应ETag)或 If-Modified-Since(对应Last-Modified),服务器返回 304 表示资源未变更
  • 对比:ETag 比 Last-Modified 更精准(避免1秒内多次修改的问题)

2. 缓存位置

  • Memory Cache:内存缓存,快速但生命周期短(随Tab关闭释放)
  • Disk Cache:磁盘缓存,容量大但读取较慢
  • Service Worker Cache:通过Service Worker拦截请求,实现离线缓存(PWA核心)
  • Push Cache:HTTP/2推送的资源缓存,优先级最低

3. 缓存策略实践

  • 静态资源(JS/CSS/图片):设置长期强缓存(如 max-age=31536000 ),并通过文件名哈希解决更新问题(如 app.a3b4c5.js
  • HTML文件:使用 no-cache 或短 max-age,确保及时更新
  • 动态接口:通常用 no-storemax-age=0

4. 如何保证资源更新?

  • 文件名哈希:Webpack配置 output.filename: '[name].[contenthash].js'
  • 覆盖式发布:先部署新资源,再更新HTML引用

5. Service Worker缓存

  • 生命周期:install (预缓存)、fetch (拦截请求)、activate(清理旧缓存)
  • 代码示例:
javascript 复制代码
self.addEventListener('install', e => {
  e.waitUntil(caches.open('v1').then(cache => cache.addAll(['/app.js'])));
});
self.addEventListener('fetch', e => e.respondWith(caches.match(e.request)));

Vue3相关的缓存优化

1. 组件缓存:<keep-alive>

  • 作用:缓存动态组件状态,避免重复渲染
  • 生命周期钩子:onActivated (激活时触发)和 onDeactivated(离开时触发)
  • 示例:
html 复制代码
<keep-alive include="HomePage">
  <component :is="currentComponent" />
</keep-alive>

2. 编译优化:静态提升

**静态节点提升:**将静态节点提取到渲染函数外,避免重复创建

javascript 复制代码
// 编译前(Vue2)
render() { return h('div', null, h('p', null, 'Static Text')) }
// 编译后(Vue3)
const hoisted = h('p', null, 'Static Text');
render() { return h('div', null, hoisted) }

**事件侦听器缓存:**缓存内联事件处理函数,避免重复生成

javascript 复制代码
// 编译前
render() { return h('button', { onClick: () => foo() }) }
// 编译后
const cachedFn = () => foo();
render() { return h('button', { onClick: cachedFn }) }

3. v-memo指令(Vue 3.2+)

  • 作用:按条件缓存子树,避免不必要的VDiff
  • 适用场景:长列表或复杂计算组件
  • 示例:
javascript 复制代码
<div v-memo="[value]"> 
  <!-- 仅当value变化时才更新 -->
  {{ expensiveCalculation() }}
</div>

4. 响应式系统优化

Proxy代替defineProperty:
  • 无需递归初始化,性能更高
  • 支持数组/对象动态属性的监听
响应式API细粒度控制:
  • **shallowRef/shallowReactive:**浅层响应式(避免深层递归开销)
  • **markRaw:**标记对象跳过Proxy代理

5. 异步组件 + Suspense

异步加载组件减少首屏体积:
javascript 复制代码
import { defineAsyncComponent } from 'vue';
const AsyncComp = defineAsyncComponent(() => import('./Foo.vue'));
<Suspense>处理加载状态:
html 复制代码
<Suspense>
  <template #default><AsyncComp /></template>
  <template #fallback>Loading...</template>
</Suspense>

6. 性能陷阱规避

  • 避免滥用响应式:大型非响应式数据用 shallowRef
  • 计算属性缓存:用 computed 替代方法调用,减少重复计算
  • 列表渲染优化:为 v-for 添加稳定 key,避免全量Diff

如何设计一个前端项目的缓存策略?

答:HTML用协商缓存,静态资源用强缓存+哈希文件名,API接口用 Cache-Control: no-store。通过Service Worker实现离线缓存。

keep-alive 的原理是什么?

答:通过Vue的抽象组件实现,缓存组件的VNode实例。激活时复用实例并触发 onActivated,避免重复渲染。

Vue3为什么比Vue2性能更好?

答:通过Proxy响应式、编译时优化(静态提升/PatchFlag)、Tree-shaking支持等,减少运行时开销。

协商缓存中ETag和Last-Modified的区别?

答:ETag通过文件哈希标识内容变化,精度高;Last-Modified依赖时间戳,可能因时间精度或内容不变修改导致失效。

相关推荐
LYFlied6 小时前
【每日算法】LeetCode 146. LRU 缓存机制
前端·数据结构·算法·leetcode·缓存
FrameNotWork9 小时前
HarmonyOS 教学实战(二):加入网络请求与缓存(让你的应用真正“像个正式 App”)
缓存·华为·harmonyos
叫我龙翔9 小时前
【Redis】从零开始掌握redis --- 认识redis
数据库·redis·缓存
源代码•宸9 小时前
goframe框架签到系统项目(安装 redis )
服务器·数据库·经验分享·redis·后端·缓存·golang
忍冬行者19 小时前
清理三主三从redis集群的过期key和键值超过10M的key
数据库·redis·缓存
TimberWill19 小时前
使用Redis队列优化内存队列
数据库·redis·缓存
梦里不知身是客111 天前
redis的缓存击穿原因
redis·缓存·bootstrap
GGBondlctrl1 天前
【Redis】从单机架构到分布式,回溯架构的成长设计美学
分布式·缓存·架构·微服务架构·单机架构
不穿格子的程序员1 天前
Redis篇4——Redis深度剖析:内存淘汰策略与缓存的三大“天坑”
数据库·redis·缓存·雪崩·内存淘汰策略
想搞艺术的程序员1 天前
Go语言环形队列:原理剖析、编程技巧与核心优势
后端·缓存·golang