聊聊web前端的缓存问题

发布于:2025-08-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

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(清理旧缓存)
  • 代码示例:
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(离开时触发)
  • 示例:
<keep-alive include="HomePage">
  <component :is="currentComponent" />
</keep-alive>

2. 编译优化:静态提升

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

// 编译前(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) }

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

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

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

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

4. 响应式系统优化

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

5. 异步组件 + Suspense

异步加载组件减少首屏体积:
import { defineAsyncComponent } from 'vue';
const AsyncComp = defineAsyncComponent(() => import('./Foo.vue'));
<Suspense>处理加载状态:
<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依赖时间戳,可能因时间精度或内容不变修改导致失效。


网站公告

今日签到

点亮在社区的每一天
去签到