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

相关推荐
YOU OU2 小时前
Redis基础常识与命令
数据库·redis·缓存
吳所畏惧6 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
Y38153266211 小时前
SERP API + Redis 缓存层:4 种方案对比与选型
数据库·redis·缓存
海兰13 小时前
【高速缓存】RedisVL 高级查询(全文搜索、混合搜索和 多向量搜索)
数据库·人工智能·redis·缓存
流星白龙1 天前
【Redis】4.基本全局命令
数据库·redis·缓存
流星白龙1 天前
【Redis】3.Redis安装与命令行客户端
数据库·redis·缓存
我不想名字重复1 天前
redis缓存和数据库数据保持一致
数据库·redis·缓存
时空无限1 天前
vllm 大模型启动缓存相关环境变量 export
linux·缓存·vllm
流星白龙2 天前
【Redis】6.计数其他命令
数据库·redis·缓存
正儿八经的少年2 天前
redis 的大 key 和热 key 详解
数据库·redis·缓存