源码资源缓存实现逻辑?

访客 源码剖析 1

本文目录导读:

  1. 核心原理
  2. 实现逻辑
  3. 关键优化策略
  4. 实际应用示例
  5. 性能监控

我来详细解释源码资源缓存的实现逻辑,这是一个在前端工程化中非常重要的优化策略。

核心原理

源码资源缓存的核心思想是:利用浏览器的缓存机制,避免重复下载未变化的资源文件,主要通过哈希HTTP缓存头来实现。

实现逻辑

文件指纹生成(构建阶段)

使用Webpack、Vite等构建工具时,会根据文件内容生成哈希值:

// webpack 配置示例
module.exports = {
  output: {
    filename: '[name].[contenthash:8].js',
    chunkFilename: '[name].[contenthash:8].chunk.js',
  }
}
// Vite 示例
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        entryFileNames: '[name].[hash].js',
        chunkFileNames: '[name].[hash].js',
      }
    }
  }
})

生成的文件名示例:

  • main.a3b8c2f1.js
  • vendor.8d7e6f5a.js
  • styles.4c3b2a1d.css

缓存策略实现

// 服务端设置缓存头(Nginx 配置示例)
location /static/ {
    # JS/CSS 等带hash的文件,设置长期缓存
    if ($uri ~* \.[0-9a-f]{8,}\.(js|css)$) {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    # HTML 文件不缓存或短期缓存
    if ($uri ~* \.html$) {
        expires -1;
        add_header Cache-Control "no-cache, must-revalidate";
    }
}

运行时缓存加载逻辑

/**
 * 源码资源缓存管理器
 */
class SourceCacheManager {
  constructor() {
    this.cache = new Map();
    this.loadingPromises = new Map();
    this.cacheVersion = '1.0';
  }
  /**
   * 加载JS资源
   */
  async loadScript(url) {
    // 1. 检查缓存
    const cached = this.cache.get(url);
    if (cached) {
      console.log('缓存命中:', url);
      return cached;
    }
    // 2. 防止并发加载
    if (this.loadingPromises.has(url)) {
      return this.loadingPromises.get(url);
    }
    // 3. 动态加载
    const promise = new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = url;
      script.onload = () => {
        this.cache.set(url, { loaded: true });
        this.loadingPromises.delete(url);
        resolve();
      };
      script.onerror = (err) => {
        this.loadingPromises.delete(url);
        reject(err);
      };
      document.head.appendChild(script);
    });
    this.loadingPromises.set(url, promise);
    return promise;
  }
  /**
   * 预加载关键资源
   */
  preloadCritical(url, as = 'script') {
    const link = document.createElement('link');
    link.rel = 'preload';
    link.href = url;
    link.as = as;
    document.head.appendChild(link);
  }
  /**
   * 清除缓存(版本更新时使用)
   */
  clearCache() {
    this.cache.clear();
    this.cacheVersion = Date.now().toString();
  }
}

Service Worker 缓存策略

// service-worker.js
const CACHE_NAME = 'source-cache-v1';
const DYNAMIC_CACHE = 'dynamic-cache-v1';
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll([
        '/static/js/main.a3b8c2f1.js',
        '/static/js/vendor.8d7e6f5a.js',
        '/static/css/styles.4c3b2a1d.css'
      ]);
    })
  );
});
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((name) => {
            return name.startsWith('source-cache-') && name !== CACHE_NAME;
          })
          .map((name) => caches.delete(name))
      );
    })
  );
});
self.addEventListener('fetch', (event) => {
  // 缓存优先策略
  if (event.request.url.includes('/static/')) {
    event.respondWith(
      caches.match(event.request).then((response) => {
        // 返回缓存的资源
        if (response) {
          return response;
        }
        // 缓存未命中,发起网络请求
        return fetch(event.request).then((networkResponse) => {
          // 只缓存成功的响应
          if (networkResponse.ok) {
            const clonedResponse = networkResponse.clone();
            caches.open(DYNAMIC_CACHE).then((cache) => {
              cache.put(event.request, clonedResponse);
            });
          }
          return networkResponse;
        });
      })
    );
  }
});

关键优化策略

版本控制与缓存失效

// 构建工具生成 manifest.json
{
  "main.js": "main.a3b8c2f1.js",
  "vendor.js": "vendor.8d7e6f5a.js",
  "styles.css": "styles.4c3b2a1d.css"
}
// 运行时检查版本
async function checkAppVersion() {
  try {
    const manifest = await fetch('/manifest.json', { cache: 'no-cache' });
    const manifestData = await manifest.json();
    // 比较版本并清除过期缓存
    if (manifestData.version !== localStorage.getItem('appVersion')) {
      await caches.delete('dynamic-cache-v1');
      localStorage.setItem('appVersion', manifestData.version);
    }
  } catch (error) {
    console.error('Failed to check app version:', error);
  }
}

分级缓存策略

class TieredCache {
  constructor() {
    this.memoryCache = new Map();     // 内存缓存
    this.storageCache = new Map();    // localStorage缓存
    this.serviceWorkerCache = null;   // SW缓存
  }
  async get(url) {
    // 1. 内存缓存 (最快)
    const memoryResult = this.memoryCache.get(url);
    if (memoryResult) return memoryResult;
    // 2. localStorage缓存
    const storageResult = localStorage.getItem(url);
    if (storageResult) {
      const parsed = JSON.parse(storageResult);
      this.memoryCache.set(url, parsed.data);
      return parsed.data;
    }
    // 3. 网络请求
    const response = await fetch(url);
    const data = await response.json();
    // 存入各级缓存
    this.memoryCache.set(url, data);
    localStorage.setItem(url, JSON.stringify({ data, timestamp: Date.now() }));
    return data;
  }
  // 内存缓存大小限制
  evictMemoryCache() {
    if (this.memoryCache.size > 50) {
      const firstKey = this.memoryCache.keys().next().value;
      this.memoryCache.delete(firstKey);
    }
  }
}

增量更新策略

class IncrementalUpdateManager {
  constructor() {
    this.updateQueue = new Set();
    this.isUpdating = false;
  }
  async checkAndUpdate() {
    // 获取当前版本资源列表
    const currentAssets = await this.getCurrentAssets();
    // 获取服务器最新资源列表
    const serverAssets = await this.getServerAssets();
    // 对比差异
    const deletedAssets = currentAssets.filter(
      url => !serverAssets.includes(url)
    );
    const newAssets = serverAssets.filter(
      url => !currentAssets.includes(url)
    );
    // 清理已删除资源的缓存
    deletedAssets.forEach(url => {
      caches.delete(url);
      localStorage.removeItem(url);
    });
    // 预加载新资源
    newAssets.forEach(url => {
      this.updateQueue.add(url);
    });
    // 批量更新
    await this.processUpdateQueue();
  }
  async processUpdateQueue() {
    if (this.isUpdating || this.updateQueue.size === 0) return;
    this.isUpdating = true;
    const batch = Array.from(this.updateQueue).slice(0, 5);
    await Promise.all(
      batch.map(url => this.loadAndCache(url))
    );
    batch.forEach(url => this.updateQueue.delete(url));
    this.isUpdating = false;
    // 继续处理剩余队列
    if (this.updateQueue.size > 0) {
      await this.processUpdateQueue();
    }
  }
}

实际应用示例

// 应用启动时的初始化
class AppInitializer {
  async initialize() {
    // 1. 检查Service Worker
    if ('serviceWorker' in navigator) {
      await navigator.serviceWorker.register('/service-worker.js');
    }
    // 2. 版本检查
    const cacheManager = new SourceCacheManager();
    await cacheManager.checkVersion();
    // 3. 预加载关键资源
    const criticalAssets = [
      '/static/js/vendor.js',
      '/static/js/main.js',
      '/static/css/styles.css'
    ];
    criticalAssets.forEach(url => cacheManager.preloadCritical(url));
    // 4. 按需加载非关键资源
    const lazyAssets = [
      '/static/js/analytics.js',
      '/static/js/third-party-lib.js'
    ];
    requestIdleCallback(() => {
      lazyAssets.forEach(url => cacheManager.loadScript(url));
    });
  }
}

性能监控

class CacheMonitor {
  constructor() {
    this.metrics = {
      hits: 0,
      misses: 0,
      size: 0
    };
  }
  recordHit() {
    this.metrics.hits++;
  }
  recordMiss() {
    this.metrics.misses++;
  }
  getHitRate() {
    const total = this.metrics.hits + this.metrics.misses;
    return total > 0 ? (this.metrics.hits / total * 100).toFixed(2) + '%' : '0%';
  }
  report() {
    console.log('缓存统计:');
    console.log(`命中率: ${this.getHitRate()}`);
    console.log(`缓存大小: ${this.metrics.size}KB`);
  }
}
  1. 哈希命名:确保文件变更时URL变化
  2. 分级缓存策略:内存 → localStorage → Service Worker
  3. 版本控制:通过manifest或版本号管理缓存失效
  4. 资源预加载:提前加载关键资源
  5. 懒加载优化:非关键资源按需加载
  6. 缓存监控:跟踪命中率和性能指标
  7. 错误处理:缓存失效时有优雅降级方案

这样实现的缓存系统可以有效减少网络请求,提升页面加载速度和用户体验。

标签: 源码资源缓存

抱歉,评论功能暂时关闭!