前端性能优化实用方案(六):智能缓存策略减少90%重复请求

前端性能优化实用方案(六):智能缓存策略减少90%重复请求

6. 数据缓存策略前端性能优化中,数据缓存是个绕不开的话题。用好了能让页面飞起来,用不好就是给自己挖坑。

缓存就像家里的冰箱,什么东西该放进去,什么时候该扔掉,这都有讲究。放错了东西,不仅占地方,还可能坏掉影响其他食物。

6.1 接口数据缓存的门道6.1.1 哪些数据适合缓存不是所有数据都适合往 localStorage 或 cookies 里塞。只有那些不怎么变化或者有固定更新周期的数据才值得缓存:

用户基础信息:token、用户名、用户ID这些系统配置:应用配置、字典数据相对静态的数据:城市列表、商品分类动态数据就别想了,比如用户的消息列表、实时股价这些,缓存了反而会误导用户。

代码语言:javascript复制// 缓存工具类 - 带过期时间的本地存储

class StorageCache {

constructor() {

this.prefix = 'app_cache_';

}

// 存储数据,默认24小时过期

set(key, value, expireTime = 24 * 60 * 60 * 1000) {

const data = {

value,

expire: Date.now() + expireTime,

timestamp: Date.now()

};

try {

localStorage.setItem(this.prefix + key, JSON.stringify(data));

return true;

} catch (error) {

console.warn('localStorage存储失败:', error);

return false;

}

}

// 获取数据,自动检查过期

get(key) {

try {

const item = localStorage.getItem(this.prefix + key);

if (!item) return null;

const data = JSON.parse(item);

// 过期了就删掉

if (Date.now() > data.expire) {

this.remove(key);

return null;

}

return data.value;

} catch (error) {

console.warn('localStorage读取失败:', error);

return null;

}

}

remove(key) {

localStorage.removeItem(this.prefix + key);

}

// 清空所有缓存

clear() {

const keys = Object.keys(localStorage);

keys.forEach(key => {

if (key.startsWith(this.prefix)) {

localStorage.removeItem(key);

}

});

}

// 检查缓存是否还有效

has(key) {

return this.get(key) !== null;

}

}

// 实际使用

const cache = new StorageCache();

// 缓存用户token,7天过期

cache.set('user_token', 'abc123', 7 * 24 * 60 * 60 * 1000);

// 缓存用户信息,1天过期

cache.set('user_info', {

id: 1001,

name: '张三',

avatar: 'avatar.jpg'

}, 24 * 60 * 60 * 1000);

// 获取缓存数据

const token = cache.get('user_token');

const userInfo = cache.get('user_info');6.1.2 API请求缓存策略有了基础的缓存工具,我们可以在API层面做更智能的缓存。

代码语言:javascript复制// API缓存管理器

class ApiCache {

constructor() {

this.cache = new StorageCache();

}

// 带缓存的请求方法

async request(url, options = {}, cacheConfig = {}) {

const {

useCache = false,

cacheTime = 5 * 60 * 1000, // 默认5分钟

cacheKey = url

} = cacheConfig;

// 先看看缓存里有没有

if (useCache) {

const cachedData = this.cache.get(cacheKey);

if (cachedData) {

console.log('从缓存获取数据:', cacheKey);

return cachedData;

}

}

try {

const response = await fetch(url, options);

const data = await response.json();

// 请求成功了就缓存起来

if (useCache && response.ok) {

this.cache.set(cacheKey, data, cacheTime);

console.log('数据已缓存:', cacheKey);

}

return data;

} catch (error) {

console.error('API请求失败:', error);

throw error;

}

}

clearCache(cacheKey) {

this.cache.remove(cacheKey);

}

clearAllCache() {

this.cache.clear();

}

}

// 使用示例

const apiCache = new ApiCache();

// 字典数据缓存30分钟,这种数据变化不频繁

const getDictData = () => {

return apiCache.request('/api/dict', {}, {

useCache: true,

cacheTime: 30 * 60 * 1000,

cacheKey: 'dict_data'

});

};

// 用户动态数据不缓存,每次都要最新的

const getUserPosts = (userId) => {

return apiCache.request(`/api/users/${userId}/posts`, {}, {

useCache: false

});

};6.2 内存缓存 - 快但不持久内存缓存就是把数据存在JavaScript的变量里,访问速度最快,但页面一刷新就没了。

这种缓存适合存放那些在当前会话中会反复使用的数据。

6.2.1 基础内存缓存实现代码语言:javascript复制// 内存缓存管理器,支持LRU淘汰策略

class MemoryCache {

constructor(maxSize = 100) {

this.cache = new Map();

this.maxSize = maxSize;

this.accessOrder = new Map(); // 记录访问顺序

}

set(key, value, ttl = 0) {

// 缓存满了就删掉最久没用的

if (this.cache.size >= this.maxSize && !this.cache.has(key)) {

this.evictLRU();

}

const item = {

value,

timestamp: Date.now(),

ttl,

expire: ttl > 0 ? Date.now() + ttl : 0

};

this.cache.set(key, item);

this.accessOrder.set(key, Date.now());

}

get(key) {

const item = this.cache.get(key);

if (!item) return null;

// 检查是否过期

if (item.expire > 0 && Date.now() > item.expire) {

this.delete(key);

return null;

}

// 更新访问时间,用于LRU

this.accessOrder.set(key, Date.now());

return item.value;

}

delete(key) {

this.cache.delete(key);

this.accessOrder.delete(key);

}

// LRU淘汰:删掉最久没访问的数据

evictLRU() {

let oldestKey = null;

let oldestTime = Date.now();

for (const [key, time] of this.accessOrder) {

if (time < oldestTime) {

oldestTime = time;

oldestKey = key;

}

}

if (oldestKey) {

this.delete(oldestKey);

}

}

clear() {

this.cache.clear();

this.accessOrder.clear();

}

// 查看缓存状态

getStats() {

return {

size: this.cache.size,

maxSize: this.maxSize,

keys: Array.from(this.cache.keys())

};

}

}

// 全局内存缓存实例

const memoryCache = new MemoryCache(50);

// 使用示例

memoryCache.set('user_1001', { name: '张三', age: 25 }, 10 * 60 * 1000); // 10分钟过期

memoryCache.set('api_result_123', { data: [...] }, 5 * 60 * 1000); // 5分钟过期

const userData = memoryCache.get('user_1001');6.2.2 Vue项目中的缓存实现在Vue项目里,我们通常把缓存放在Vuex store中管理。

代码语言:javascript复制// store/modules/cache.js

const state = {

apiCache: new Map(),

userCache: new Map(),

configCache: new Map()

};

const mutations = {

SET_API_CACHE(state, { key, data, expire = 0 }) {

state.apiCache.set(key, {

data,

timestamp: Date.now(),

expire: expire > 0 ? Date.now() + expire : 0

});

},

SET_USER_CACHE(state, { key, data }) {

state.userCache.set(key, {

data,

timestamp: Date.now()

});

},

CLEAR_CACHE(state, cacheType) {

if (cacheType === 'api') {

state.apiCache.clear();

} else if (cacheType === 'user') {

state.userCache.clear();

} else if (cacheType === 'all') {

state.apiCache.clear();

state.userCache.clear();

state.configCache.clear();

}

}

};

const getters = {

getApiCache: (state) => (key) => {

const item = state.apiCache.get(key);

if (!item) return null;

// 检查过期

if (item.expire > 0 && Date.now() > item.expire) {

state.apiCache.delete(key);

return null;

}

return item.data;

},

getUserCache: (state) => (key) => {

const item = state.userCache.get(key);

return item ? item.data : null;

}

};

const actions = {

// 带缓存的API请求

async fetchWithCache({ commit, getters }, { url, cacheKey, cacheTime = 5 * 60 * 1000 }) {

// 先从缓存找

const cachedData = getters.getApiCache(cacheKey);

if (cachedData) {

return cachedData;

}

try {

const response = await fetch(url);

const data = await response.json();

// 存到缓存

commit('SET_API_CACHE', {

key: cacheKey,

data,

expire: cacheTime

});

return data;

} catch (error) {

console.error('API请求失败:', error);

throw error;

}

}

};

export default {

namespaced: true,

state,

mutations,

getters,

actions

};6.2.3 React项目中的缓存HookReact项目里,我们可以封装一个自定义Hook来处理缓存逻辑。

代码语言:javascript复制// hooks/useCache.js

import { useState, useEffect, useRef } from 'react';

// 全局缓存,所有组件共享

const globalCache = new Map();

export const useCache = (key, fetcher, options = {}) => {

const {

cacheTime = 5 * 60 * 1000, // 默认5分钟

staleTime = 0, // 数据新鲜时间

enabled = true

} = options;

const [data, setData] = useState(null);

const [loading, setLoading] = useState(false);

const [error, setError] = useState(null);

const mountedRef = useRef(true);

useEffect(() => {

return () => {

mountedRef.current = false;

};

}, []);

const fetchData = async (forceRefresh = false) => {

if (!enabled) return;

const cached = globalCache.get(key);

const now = Date.now();

if (!forceRefresh && cached) {

const isStale = now - cached.timestamp > staleTime;

const isExpired = now > cached.expire;

if (!isExpired) {

setData(cached.data);

// 数据有点旧了,后台偷偷更新一下

if (isStale) {

fetchFreshData();

}

return;

}

}

await fetchFreshData();

};

const fetchFreshData = async () => {

setLoading(true);

setError(null);

try {

const result = await fetcher();

if (mountedRef.current) {

setData(result);

// 更新缓存

globalCache.set(key, {

data: result,

timestamp: Date.now(),

expire: Date.now() + cacheTime

});

}

} catch (err) {

if (mountedRef.current) {

setError(err);

}

} finally {

if (mountedRef.current) {

setLoading(false);

}

}

};

useEffect(() => {

fetchData();

}, [key, enabled]);

const refetch = () => fetchData(true);

const invalidate = () => globalCache.delete(key);

return {

data,

loading,

error,

refetch,

invalidate

};

};

// 使用示例

const UserProfile = ({ userId }) => {

const {

data: user,

loading,

error,

refetch

} = useCache(

`user_${userId}`,

() => fetch(`/api/users/${userId}`).then(res => res.json()),

{

cacheTime: 10 * 60 * 1000, // 10分钟缓存

staleTime: 2 * 60 * 1000 // 2分钟内认为数据新鲜

}

);

if (loading) return

加载中...
;

if (error) return

加载失败
;

return (

{user?.name}

);

};6.3 智能缓存策略实际项目中,我们往往需要结合多种缓存策略。内存缓存速度快,持久化缓存能保存更久,组合使用效果最好。

代码语言:javascript复制// 智能缓存管理器 - 多级缓存

class SmartCache {

constructor() {

this.memoryCache = new MemoryCache(100);

this.storageCache = new StorageCache();

this.requestQueue = new Map(); // 防止重复请求

}

async get(key, fetcher, options = {}) {

const {

cacheLevel = 'memory', // 'memory' | 'storage' | 'both'

cacheTime = 5 * 60 * 1000,

forceRefresh = false

} = options;

if (!forceRefresh) {

// 第一层:内存缓存

if (cacheLevel === 'memory' || cacheLevel === 'both') {

const memoryData = this.memoryCache.get(key);

if (memoryData) {

return memoryData;

}

}

// 第二层:持久化缓存

if (cacheLevel === 'storage' || cacheLevel === 'both') {

const storageData = this.storageCache.get(key);

if (storageData) {

// 同时放到内存缓存里,下次更快

this.memoryCache.set(key, storageData, cacheTime);

return storageData;

}

}

}

// 防止同时发起多个相同请求

if (this.requestQueue.has(key)) {

return this.requestQueue.get(key);

}

const promise = this.fetchAndCache(key, fetcher, options);

this.requestQueue.set(key, promise);

try {

const result = await promise;

return result;

} finally {

this.requestQueue.delete(key);

}

}

async fetchAndCache(key, fetcher, options) {

const { cacheLevel = 'memory', cacheTime = 5 * 60 * 1000 } = options;

try {

const data = await fetcher();

// 根据配置存储到不同级别的缓存

if (cacheLevel === 'memory' || cacheLevel === 'both') {

this.memoryCache.set(key, data, cacheTime);

}

if (cacheLevel === 'storage' || cacheLevel === 'both') {

this.storageCache.set(key, data, cacheTime);

}

return data;

} catch (error) {

console.error('数据获取失败:', error);

throw error;

}

}

// 清除指定缓存

clear(key, level = 'both') {

if (level === 'memory' || level === 'both') {

this.memoryCache.delete(key);

}

if (level === 'storage' || level === 'both') {

this.storageCache.remove(key);

}

}

}

// 全局智能缓存实例

const smartCache = new SmartCache();

// 使用示例

const getUserData = async (userId) => {

return smartCache.get(

`user_${userId}`,

() => fetch(`/api/users/${userId}`).then(res => res.json()),

{

cacheLevel: 'both',

cacheTime: 30 * 60 * 1000 // 30分钟

}

);

};

const getConfigData = async () => {

return smartCache.get(

'app_config',

() => fetch('/api/config').then(res => res.json()),

{

cacheLevel: 'storage',

cacheTime: 24 * 60 * 60 * 1000 // 24小时

}

);

};小结选择合适的缓存类型

内存缓存:适合频繁访问的临时数据优点:速度最快缺点:页面刷新就没了适用:API结果、计算结果、组件状态持久化缓存:适合长期有效的数据优点:能保存很久缺点:存储空间有限,可能被浏览器清理适用:用户信息、配置数据、字典数据多级缓存:结合两者优势内存缓存做一级缓存持久化缓存做二级缓存提供最佳性能和用户体验通过合理的缓存策略,可以大幅减少网络请求,让页面响应更快,用户体验更好。但要记住,缓存是为了提升性能,不能因为缓存导致数据不一致的问题。

你可能也喜欢

温湿度传感器的类型:环境监测的关键设备
365bet官网地址

温湿度传感器的类型:环境监测的关键设备

📅 01-05 👀 4111
《抱朴子》·疾谬原文译文翻译 《外篇》
365bet官网地址

《抱朴子》·疾谬原文译文翻译 《外篇》

📅 07-10 👀 8473
2025 菲律宾旅游景点推荐:十大必去岛屿与城市
365bet官网地址

2025 菲律宾旅游景点推荐:十大必去岛屿与城市

📅 01-03 👀 6977
车上为什么会有蟑螂
beat365中文官网

车上为什么会有蟑螂

📅 08-16 👀 9983
生存战争怎么双人联机
365bet官网地址

生存战争怎么双人联机

📅 08-30 👀 1807
女医明妃传
36500365体育在线投注

女医明妃传

📅 01-09 👀 3820