聊聊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依赖时间戳,可能因时间精度或内容不变修改导致失效。

相关推荐
天疆说3 小时前
04 稳定性与性能调优实录:OOM 崩溃、并发槽位、前缀缓存与 DSpark 调参
linux·运维·缓存
csdn2015_3 小时前
springboot +mybatis 查询myql开启程序级别缓存
spring boot·缓存·mybatis
隔窗听雨眠3 小时前
缓存增强生成CAG:用预加载KV缓存突破RAG实时检索瓶颈
缓存
光影少年4 小时前
react离线缓存、图片缓存方案
开发语言·前端·javascript·react native·react.js·缓存·前端框架
m0_3807438715 小时前
给大模型调用加一层本地缓存,让重复请求直接命中磁盘
缓存
FakeOccupational17 小时前
【电路笔记 STM32】Cortex-M7 内核上的数据缓存(D-Cache)结构+MPU+DMA&Cache+STM32CubeMX配置
笔记·stm32·缓存
l1t1 天前
DeepSeek总结的在 pg_stat_statements 中诊断高基数工作负载
数据库·缓存·postgresql
2401_894915531 天前
新手落地 Geo 优化:源码下载、依赖安装、数据库初始化完整步骤
运维·服务器·数据库·人工智能·缓存·开源
2401_894915531 天前
Geo 优化源码部署避坑指南:解决访问异常、定位失效、收录卡顿问题
java·服务器·后端·缓存·开源
祈禾2 天前
Redis三大缓存问题与分布式锁
运维·数据库·redis·笔记·分布式·缓存