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