HarmonyOS开发中数据缓存策略:网络请求缓存

举报
Jack20 发表于 2026/06/25 21:27:15 2026/06/25
【摘要】 数据缓存策略:网络请求缓存缓存用得好,流量省一半,体验快一倍 一、背景与动机:为什么需要网络请求缓存?打开一个 App,首页加载了商品列表。用户点进商品详情,又返回首页——商品列表又重新加载了一遍。明明刚才已经加载过了,为什么还要浪费流量和时间?这就是缓存要解决的问题:节省流量:相同请求不重复发送提升速度:直接返回缓存数据,毫秒级响应离线可用:没网也能看缓存数据降低负载:减少服务器压力 缓...

数据缓存策略:网络请求缓存

缓存用得好,流量省一半,体验快一倍

一、背景与动机:为什么需要网络请求缓存?

打开一个 App,首页加载了商品列表。用户点进商品详情,又返回首页——商品列表又重新加载了一遍

明明刚才已经加载过了,为什么还要浪费流量和时间?

这就是缓存要解决的问题:

  1. 节省流量:相同请求不重复发送
  2. 提升速度:直接返回缓存数据,毫秒级响应
  3. 离线可用:没网也能看缓存数据
  4. 降低负载:减少服务器压力

缓存策略有哪些?

策略 说明 适用场景
Cache-First 优先缓存,缓存没有才请求 静态资源、不常变数据
Network-First 优先网络,网络失败才用缓存 实时数据、新闻列表
Stale-While-Revalidate 先返回缓存,后台更新 列表数据、用户信息
Network-Only 只用网络,不缓存 敏感数据、实时状态
Cache-Only 只用缓存 离线场景

二、核心原理:缓存分层架构

缓存不是"存了就完事",而是分层的体系:

flowchart TB
    A[请求发起] --> B{检查内存缓存}
    
    B -->|命中| C[返回内存缓存]
    B -->|未命中| D{检查磁盘缓存}
    
    D -->|命中| E[读取磁盘缓存]
    E --> F[写入内存缓存]
    F --> G[返回数据]
    
    D -->|未命中| H[发起网络请求]
    H --> I{请求成功?}
    
    I -->|成功| J[写入磁盘缓存]
    J --> K[写入内存缓存]
    K --> L[返回数据]
    
    I -->|失败| M{有缓存?}
    M -->|| N[返回过期缓存]
    M -->|| O[返回错误]
    
    classDef primary fill:#4A90E2,stroke:#2E5C8A,color:#fff
    classDef warning fill:#F5A623,stroke:#C17D10,color:#fff
    classDef error fill:#E74C3C,stroke:#C0392B,color:#fff
    classDef info fill:#7ED321,stroke:#5BA318,color:#fff
    
    class A,C,G,L primary
    class B,D,I,M info
    class E,F,J,K warning
    class H,N,O error

三层缓存

  1. 内存缓存:Map 结构,最快,但易丢失
  2. 磁盘缓存:Preferences/RDB,持久化,稍慢
  3. 网络请求:最慢,但数据最新

三、代码实战:完整缓存系统实现

示例1:缓存配置与类型定义

// network/cache/types.ets

/**
 * 缓存策略枚举
 */
export enum CacheStrategy {
  /** 优先缓存,缓存没有才请求 */
  CACHE_FIRST = 'cache-first',
  /** 优先网络,网络失败才用缓存 */
  NETWORK_FIRST = 'network-first',
  /** 先返回缓存,后台更新 */
  STALE_WHILE_REVALIDATE = 'stale-while-revalidate',
  /** 只用网络,不缓存 */
  NETWORK_ONLY = 'network-only',
  /** 只用缓存 */
  CACHE_ONLY = 'cache-only'
}

/**
 * 缓存项配置
 */
export interface CacheConfig {
  /** 缓存键(默认使用请求URL) */
  key?: string;
  /** 缓存策略 */
  strategy?: CacheStrategy;
  /** 缓存时长(毫秒),0表示永久 */
  maxAge?: number;
  /** 是否强制刷新(忽略缓存) */
  forceRefresh?: boolean;
  /** 网络失败时是否使用过期缓存 */
  useExpiredCache?: boolean;
  /** 是否缓存到磁盘 */
  persist?: boolean;
}

/**
 * 缓存项结构
 */
export interface CacheItem<T = any> {
  /** 缓存数据 */
  data: T;
  /** 缓存时间 */
  timestamp: number;
  /** 过期时间 */
  expires: number;
  /** 缓存键 */
  key: string;
  /** 响应头(用于ETag/Last-Modified验证) */
  headers?: Record<string, string>;
  /** 是否来自缓存 */
  fromCache: boolean;
}

/**
 * 缓存统计信息
 */
export interface CacheStats {
  /** 内存缓存数量 */
  memoryCount: number;
  /** 磁盘缓存数量 */
  diskCount: number;
  /** 内存缓存大小(字节) */
  memorySize: number;
  /** 磁盘缓存大小(字节) */
  diskSize: number;
  /** 命中次数 */
  hitCount: number;
  /** 未命中次数 */
  missCount: number;
}

示例2:内存缓存实现

// network/cache/MemoryCache.ets
import { CacheItem, CacheStats } from './types';

/**
 * 内存缓存
 * 使用Map实现,读写速度最快
 * 特点:应用重启后丢失
 */
export class MemoryCache {
  private cache: Map<string, CacheItem> = new Map();
  private maxSize: number; // 最大缓存数量
  private hitCount: number = 0;
  private missCount: number = 0;

  constructor(maxSize: number = 100) {
    this.maxSize = maxSize;
  }

  /**
   * 获取缓存
   * @param key 缓存键
   * @returns 缓存项或null
   */
  get<T>(key: string): CacheItem<T> | null {
    const item = this.cache.get(key);

    if (!item) {
      this.missCount++;
      return null;
    }

    // 检查是否过期
    if (this.isExpired(item)) {
      this.cache.delete(key);
      this.missCount++;
      return null;
    }

    this.hitCount++;
    return item as CacheItem<T>;
  }

  /**
   * 设置缓存
   * @param key 缓存键
   * @param data 缓存数据
   * @param maxAge 缓存时长(毫秒)
   */
  set<T>(key: string, data: T, maxAge: number = 0): void {
    // 检查是否超过最大数量
    if (this.cache.size >= this.maxSize) {
      this.evict(); // 淘汰旧缓存
    }

    const now = Date.now();
    const item: CacheItem<T> = {
      data,
      timestamp: now,
      expires: maxAge > 0 ? now + maxAge : Number.MAX_SAFE_INTEGER,
      key,
      fromCache: true
    };

    this.cache.set(key, item);
  }

  /**
   * 删除缓存
   */
  delete(key: string): boolean {
    return this.cache.delete(key);
  }

  /**
   * 清空所有缓存
   */
  clear(): void {
    this.cache.clear();
  }

  /**
   * 检查缓存是否存在
   */
  has(key: string): boolean {
    const item = this.cache.get(key);
    if (!item) return false;
    if (this.isExpired(item)) {
      this.cache.delete(key);
      return false;
    }
    return true;
  }

  /**
   * 获取所有缓存键
   */
  keys(): string[] {
    return Array.from(this.cache.keys());
  }

  /**
   * 获取缓存统计信息
   */
  getStats(): CacheStats {
    let memorySize = 0;
    this.cache.forEach(item => {
      memorySize += this.estimateSize(item.data);
    });

    return {
      memoryCount: this.cache.size,
      diskCount: 0,
      memorySize,
      diskSize: 0,
      hitCount: this.hitCount,
      missCount: this.missCount
    };
  }

  /**
   * 检查是否过期
   * @private
   */
  private isExpired(item: CacheItem): boolean {
    return Date.now() > item.expires;
  }

  /**
   * 淘汰旧缓存(LRU策略)
   * @private
   */
  private evict(): void {
    // 找到最旧的缓存项
    let oldestKey: string | null = null;
    let oldestTime = Number.MAX_SAFE_INTEGER;

    this.cache.forEach((item, key) => {
      if (item.timestamp < oldestTime) {
        oldestTime = item.timestamp;
        oldestKey = key;
      }
    });

    if (oldestKey) {
      this.cache.delete(oldestKey);
      console.info(`[MemoryCache] 淘汰缓存: ${oldestKey}`);
    }
  }

  /**
   * 估算数据大小
   * @private
   */
  private estimateSize(data: any): number {
    try {
      return JSON.stringify(data).length * 2; // UTF-16编码
    } catch {
      return 0;
    }
  }
}

示例3:磁盘缓存实现

// network/cache/DiskCache.ets
import { preferences } from '@kit.ArkData';
import { CacheItem } from './types';

/**
 * 磁盘缓存
 * 使用Preferences实现持久化存储
 * 特点:应用重启后保留
 */
export class DiskCache {
  private store: preferences.Preferences | null = null;
  private storeName: string = 'network_cache';
  private prefix: string = 'cache_';

  /**
   * 初始化磁盘缓存
   */
  async init(): Promise<void> {
    try {
      this.store = await preferences.getPreferences(this.storeName);
      console.info('[DiskCache] 初始化完成');
    } catch (error) {
      console.error('[DiskCache] 初始化失败:', error);
    }
  }

  /**
   * 获取缓存
   */
  async get<T>(key: string): Promise<CacheItem<T> | null> {
    if (!this.store) {
      await this.init();
    }

    try {
      const json = await this.store!.get(this.prefix + key, '') as string;
      if (!json) {
        return null;
      }

      const item: CacheItem<T> = JSON.parse(json);

      // 检查是否过期
      if (Date.now() > item.expires) {
        await this.delete(key);
        return null;
      }

      return item;
    } catch (error) {
      console.error(`[DiskCache] 读取缓存失败 ${key}:`, error);
      return null;
    }
  }

  /**
   * 设置缓存
   */
  async set<T>(key: string, data: T, maxAge: number = 0): Promise<void> {
    if (!this.store) {
      await this.init();
    }

    const now = Date.now();
    const item: CacheItem<T> = {
      data,
      timestamp: now,
      expires: maxAge > 0 ? now + maxAge : Number.MAX_SAFE_INTEGER,
      key,
      fromCache: true
    };

    try {
      const json = JSON.stringify(item);
      await this.store!.put(this.prefix + key, json);
      await this.store!.flush();
    } catch (error) {
      console.error(`[DiskCache] 写入缓存失败 ${key}:`, error);
    }
  }

  /**
   * 删除缓存
   */
  async delete(key: string): Promise<void> {
    if (!this.store) {
      await this.init();
    }

    try {
      await this.store!.delete(this.prefix + key);
      await this.store!.flush();
    } catch (error) {
      console.error(`[DiskCache] 删除缓存失败 ${key}:`, error);
    }
  }

  /**
   * 清空所有缓存
   */
  async clear(): Promise<void> {
    if (!this.store) {
      await this.init();
    }

    try {
      // 获取所有键
      const allKeys = await this.store!.getAll();
      
      // 删除缓存相关键
      for (const key of Object.keys(allKeys)) {
        if (key.startsWith(this.prefix)) {
          await this.store!.delete(key);
        }
      }
      
      await this.store!.flush();
      console.info('[DiskCache] 已清空所有缓存');
    } catch (error) {
      console.error('[DiskCache] 清空缓存失败:', error);
    }
  }

  /**
   * 获取所有缓存键
   */
  async keys(): Promise<string[]> {
    if (!this.store) {
      await this.init();
    }

    const allKeys = await this.store!.getAll();
    return Object.keys(allKeys)
      .filter(key => key.startsWith(this.prefix))
      .map(key => key.substring(this.prefix.length));
  }
}

示例4:缓存管理器

// network/cache/CacheManager.ets
import { MemoryCache } from './MemoryCache';
import { DiskCache } from './DiskCache';
import { CacheConfig, CacheStrategy, CacheItem } from './types';

/**
 * 缓存管理器
 * 统一管理内存缓存和磁盘缓存
 */
export class CacheManager {
  private memoryCache: MemoryCache;
  private diskCache: DiskCache;
  private defaultMaxAge: number = 5 * 60 * 1000; // 默认5分钟

  constructor(config?: { maxSize?: number; defaultMaxAge?: number }) {
    this.memoryCache = new MemoryCache(config?.maxSize || 100);
    this.diskCache = new DiskCache();
    
    if (config?.defaultMaxAge) {
      this.defaultMaxAge = config.defaultMaxAge;
    }
  }

  /**
   * 初始化缓存管理器
   */
  async init(): Promise<void> {
    await this.diskCache.init();
    console.info('[CacheManager] 初始化完成');
  }

  /**
   * 生成缓存键
   * @private
   */
  private generateKey(url: string, params?: any): string {
    const paramString = params ? JSON.stringify(params) : '';
    const raw = `${url}:${paramString}`;
    
    // 使用简单哈希(实际项目可以用MD5)
    let hash = 0;
    for (let i = 0; i < raw.length; i++) {
      hash = ((hash << 5) - hash) + raw.charCodeAt(i);
      hash = hash & hash; // 转为32位整数
    }
    
    return `req_${Math.abs(hash)}`;
  }

  /**
   * 获取缓存
   */
  async get<T>(key: string, config?: CacheConfig): Promise<CacheItem<T> | null> {
    // 1. 检查内存缓存
    const memoryItem = this.memoryCache.get<T>(key);
    if (memoryItem) {
      console.info(`[CacheManager] 内存缓存命中: ${key}`);
      return memoryItem;
    }

    // 2. 检查磁盘缓存(如果配置了持久化)
    if (config?.persist !== false) {
      const diskItem = await this.diskCache.get<T>(key);
      if (diskItem) {
        console.info(`[CacheManager] 磁盘缓存命中: ${key}`);
        // 写入内存缓存
        this.memoryCache.set(key, diskItem.data, diskItem.expires - Date.now());
        return diskItem;
      }
    }

    return null;
  }

  /**
   * 设置缓存
   */
  async set<T>(key: string, data: T, config?: CacheConfig): Promise<void> {
    const maxAge = config?.maxAge ?? this.defaultMaxAge;

    // 写入内存缓存
    this.memoryCache.set(key, data, maxAge);

    // 写入磁盘缓存(如果配置了持久化)
    if (config?.persist !== false) {
      await this.diskCache.set(key, data, maxAge);
    }

    console.info(`[CacheManager] 缓存已写入: ${key}, 有效期: ${maxAge}ms`);
  }

  /**
   * 删除缓存
   */
  async delete(key: string): Promise<void> {
    this.memoryCache.delete(key);
    await this.diskCache.delete(key);
  }

  /**
   * 清空所有缓存
   */
  async clear(): Promise<void> {
    this.memoryCache.clear();
    await this.diskCache.clear();
    console.info('[CacheManager] 所有缓存已清空');
  }

  /**
   * 获取缓存统计信息
   */
  getStats() {
    return this.memoryCache.getStats();
  }

  /**
   * 执行带缓存的请求
   * @param requestFn 实际请求函数
   * @param url 请求URL
   * @param params 请求参数
   * @param config 缓存配置
   */
  async executeWithCache<T>(
    requestFn: () => Promise<T>,
    url: string,
    params?: any,
    config?: CacheConfig
  ): Promise<{ data: T; fromCache: boolean }> {
    // 生成缓存键
    const key = config?.key || this.generateKey(url, params);
    
    // 强制刷新时跳过缓存
    if (!config?.forceRefresh) {
      // 获取缓存
      const cached = await this.get<T>(key, config);
      
      if (cached && config?.strategy !== CacheStrategy.NETWORK_ONLY) {
        // 根据策略处理
        if (config?.strategy === CacheStrategy.CACHE_FIRST ||
            config?.strategy === CacheStrategy.CACHE_ONLY) {
          return { data: cached.data, fromCache: true };
        }
        
        // Stale-While-Revalidate:先返回缓存,后台更新
        if (config?.strategy === CacheStrategy.STALE_WHILE_REVALIDATE) {
          // 触发后台更新(不等待)
          this.backgroundUpdate(key, requestFn, config);
          return { data: cached.data, fromCache: true };
        }
      }
    }

    // Network-Only 或 缓存未命中 或 强制刷新
    if (config?.strategy === CacheStrategy.CACHE_ONLY) {
      throw new Error('No cache available');
    }

    try {
      // 发起网络请求
      const data = await requestFn();
      
      // 写入缓存(除非是NETWORK_ONLY)
      if (config?.strategy !== CacheStrategy.NETWORK_ONLY) {
        await this.set(key, data, config);
      }
      
      return { data, fromCache: false };
    } catch (error) {
      // 网络失败,尝试使用过期缓存
      if (config?.useExpiredCache) {
        const cached = await this.getExpiredCache<T>(key);
        if (cached) {
          console.warn(`[CacheManager] 网络失败,使用过期缓存: ${key}`);
          return { data: cached.data, fromCache: true };
        }
      }
      
      throw error;
    }
  }

  /**
   * 后台更新缓存
   * @private
   */
  private async backgroundUpdate<T>(
    key: string,
    requestFn: () => Promise<T>,
    config?: CacheConfig
  ): Promise<void> {
    try {
      const data = await requestFn();
      await this.set(key, data, config);
      console.info(`[CacheManager] 后台更新完成: ${key}`);
    } catch (error) {
      console.error(`[CacheManager] 后台更新失败: ${key}`, error);
    }
  }

  /**
   * 获取过期缓存(用于降级)
   * @private
   */
  private async getExpiredCache<T>(key: string): Promise<CacheItem<T> | null> {
    // 从磁盘获取(不检查过期)
    const diskItem = await this.diskCache.get<T>(key);
    return diskItem;
  }
}

示例5:缓存拦截器

// network/cache/CacheInterceptor.ets
import { Interceptor, Response, RequestConfig } from '../types';
import { CacheManager, CacheConfig, CacheStrategy } from './CacheManager';

/**
 * 缓存拦截器
 * 在请求前检查缓存,在响应后写入缓存
 */
export class CacheInterceptor implements Interceptor {
  private cacheManager: CacheManager;

  constructor(cacheManager: CacheManager) {
    this.cacheManager = cacheManager;
  }

  /**
   * 请求拦截:检查缓存
   */
  async beforeRequest(config: RequestConfig): Promise<RequestConfig> {
    // 获取缓存配置
    const cacheConfig = (config as any).__cacheConfig as CacheConfig | undefined;
    
    if (!cacheConfig || cacheConfig.strategy === CacheStrategy.NETWORK_ONLY) {
      return config;
    }

    // 标记是否需要检查缓存
    (config as any).__checkCache = true;
    
    return config;
  }

  /**
   * 响应拦截:写入缓存
   */
  async afterResponse<T>(response: Response<T>): Promise<Response<T>> {
    const config = response.config;
    const cacheConfig = (config as any).__cacheConfig as CacheConfig | undefined;
    
    if (!cacheConfig || cacheConfig.strategy === CacheStrategy.NETWORK_ONLY) {
      return response;
    }

    // 写入缓存
    const key = cacheConfig.key || this.generateKey(config.url, config.params);
    await this.cacheManager.set(key, response.data, cacheConfig);

    return response;
  }

  /**
   * 生成缓存键
   * @private
   */
  private generateKey(url: string, params?: any): string {
    const paramString = params ? JSON.stringify(params) : '';
    const raw = `${url}:${paramString}`;
    
    let hash = 0;
    for (let i = 0; i < raw.length; i++) {
      hash = ((hash << 5) - hash) + raw.charCodeAt(i);
      hash = hash & hash;
    }
    
    return `req_${Math.abs(hash)}`;
  }
}

示例6:实际使用示例

// api/ProductAPI.ets
import { HttpClient } from '../network/HttpClient';
import { CacheManager, CacheStrategy } from '../network/cache';

export class ProductAPI {
  private http: HttpClient;
  private cacheManager: CacheManager;

  constructor(http: HttpClient, cacheManager: CacheManager) {
    this.http = http;
    this.cacheManager = cacheManager;
  }

  /**
   * 获取商品列表(带缓存)
   * 策略:Stale-While-Revalidate
   */
  async getProductList(category?: string): Promise<Response<Product[]>> {
    return this.cacheManager.executeWithCache(
      () => this.http.get<Product[]>('/products', { category }),
      '/products',
      { category },
      {
        strategy: CacheStrategy.STALE_WHILE_REVALIDATE,
        maxAge: 5 * 60 * 1000, // 5分钟
        persist: true
      }
    ).then(result => ({
      data: result.data,
      status: 200,
      headers: {},
      config: {} as any
    }));
  }

  /**
   * 获取商品详情(带缓存)
   * 策略:Cache-First
   */
  async getProductDetail(id: string): Promise<Response<Product>> {
    return this.cacheManager.executeWithCache(
      () => this.http.get<Product>(`/products/${id}`),
      `/products/${id}`,
      {},
      {
        strategy: CacheStrategy.CACHE_FIRST,
        maxAge: 10 * 60 * 1000, // 10分钟
        persist: true
      }
    ).then(result => ({
      data: result.data,
      status: 200,
      headers: {},
      config: {} as any
    }));
  }

  /**
   * 搜索商品(不缓存)
   */
  async searchProducts(keyword: string): Promise<Response<Product[]>> {
    return this.http.get<Product[]>('/products/search', { keyword });
  }

  /**
   * 强制刷新商品列表
   */
  async refreshProductList(category?: string): Promise<Response<Product[]>> {
    return this.cacheManager.executeWithCache(
      () => this.http.get<Product[]>('/products', { category }),
      '/products',
      { category },
      {
        strategy: CacheStrategy.NETWORK_FIRST,
        forceRefresh: true,
        maxAge: 5 * 60 * 1000
      }
    ).then(result => ({
      data: result.data,
      status: 200,
      headers: {},
      config: {} as any
    }));
  }
}

四、踩坑与注意事项

坑1:缓存键冲突

问题:不同参数的请求生成了相同的缓存键。

// ❌ 错误:只用URL做key
const key = url; // /products?page=1 和 /products?page=2 会冲突

// ✅ 正确:URL + 参数
const key = `${url}:${JSON.stringify(params)}`;

坑2:缓存数据被意外修改

问题:缓存返回的是引用,外部修改会影响缓存。

// ❌ 错误:直接修改缓存数据
const data = await cache.get('key');
data.name = 'modified'; // 缓存也被修改了

// ✅ 正确:返回深拷贝
get<T>(key: string): T | null {
  const item = this.cache.get(key);
  return item ? JSON.parse(JSON.stringify(item.data)) : null;
}

坑3:缓存占用内存过大

问题:缓存太多数据,导致内存溢出。

解决:设置最大缓存数量,使用 LRU 淘汰:

// 设置最大缓存数量
const cache = new MemoryCache(100); // 最多100条

// 定期清理过期缓存
setInterval(() => {
  cache.cleanup();
}, 60 * 1000); // 每分钟清理一次

坑4:敏感数据被缓存

问题:用户信息、token 等敏感数据被缓存到磁盘。

解决:敏感数据标记为不持久化:

await cacheManager.set('user-info', userData, {
  persist: false, // 不写入磁盘
  maxAge: 60 * 1000 // 1分钟后过期
});

五、HarmonyOS 6 适配要点

1. Preferences API 增强

// HarmonyOS 6 支持更大的存储空间
const store = await preferences.getPreferences('cache', {
  // ✨ 新增:指定存储文件路径
  filePath: '/data/storage/el2/base/preferences/'
});

// ✨ 新增:批量操作
await store.putBatch([
  { key: 'cache_1', value: 'data1' },
  { key: 'cache_2', value: 'data2' }
]);

2. 缓存大小限制

// HarmonyOS 6 可以获取存储配额
const info = await preferences.getStorageInfo();
console.log('已使用:', info.usedSpace);
console.log('总配额:', info.totalSpace);

3. 缓存加密

// HarmonyOS 6 支持加密存储
import { cryptoFramework } from '@kit.CryptoArchitectureKit';

// 加密缓存数据
async setEncrypted(key: string, data: any): Promise<void> {
  const json = JSON.stringify(data);
  const encrypted = await this.encrypt(json);
  await this.store.put(key, encrypted);
}

// 解密缓存数据
async getDecrypted<T>(key: string): Promise<T | null> {
  const encrypted = await this.store.get(key, '');
  if (!encrypted) return null;
  const json = await this.decrypt(encrypted);
  return JSON.parse(json);
}

六、总结

缓存是网络层的"加速器",用好缓存能大幅提升用户体验:

策略 优点 缺点 适用场景
Cache-First 速度最快 数据可能旧 静态资源、配置
Network-First 数据最新 速度慢 实时数据
Stale-While-Revalidate 兼顾速度和新鲜度 实现复杂 列表数据
Network-Only 数据最新 无缓存优势 敏感数据
Cache-Only 离线可用 数据可能旧 离线场景

记住几个原则:

  • ✅ 缓存键要唯一,避免冲突
  • ✅ 缓存数据要深拷贝,避免污染
  • ✅ 缓存大小要限制,避免内存溢出
  • ✅ 敏感数据不持久化,避免泄露
  • ✅ 过期缓存要清理,避免占用空间

下一篇我们深入离线优先策略,看看如何让应用在无网环境下也能优雅运行。


💡 最佳实践提示:建议根据数据类型设置不同的缓存策略:静态资源用 Cache-First,用户数据用 Network-First,列表数据用 Stale-While-Revalidate。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。