全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-16 8 分钟 ✍️ juanwangdev

请求合并与缓存策略

减少HTTP请求次数和有效利用缓存是前端性能优化的关键手段。

请求合并策略

资源合并

JavaScript
// ❌ 多个小文件单独请求
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>

// ✅ 合并为单个文件
<script src="bundle.js"></script>

// Webpack配置
module.exports = {
  optimization: {
    splitChunks: {
      minSize: 20000, // 小于20KB不分割,保持合并
    }
  }
};

图片合并(Sprite)

JavaScript
// CSS Sprite
.icon-home {
  background-image: url('sprite.png');
  background-position: 0 0;
  width: 20px;
  height: 20px;
}

.icon-user {
  background-image: url('sprite.png');
  background-position: -20px 0;
  width: 20px;
  height: 20px;
}

// 工具生成
// webpack-spritesmith / gulp.spritesmith

API请求合并

JavaScript
// ❌ 多次单独请求
const user = await fetch('/api/user/1');
const profile = await fetch('/api/profile/1');
const orders = await fetch('/api/orders/1');

// ✅ 批量请求API
const data = await fetch('/api/batch', {
  method: 'POST',
  body: JSON.stringify({
    requests: [
      { path: '/user/1' },
      { path: '/profile/1' },
      { path: '/orders/1' }
    ]
  })
});

// GraphQL天然合并
const query = `
  query {
    user(id: 1) { name email }
    profile(id: 1) { avatar bio }
    orders(userId: 1) { id total }
  }
`;

请求队列合并

JavaScript
class RequestQueue {
  constructor(batchFn, delay = 50) {
    this.queue = [];
    this.batchFn = batchFn;
    this.delay = delay;
    this.timer = null;
  }

  add(request) {
    return new Promise((resolve, reject) => {
      this.queue.push({ request, resolve, reject });

      if (!this.timer) {
        this.timer = setTimeout(() => this.flush(), this.delay);
      }
    });
  }

  flush() {
    this.timer = null;

    if (this.queue.length === 0) return;

    const batch = this.queue;
    this.queue = [];

    this.batchFn(batch.map(item => item.request))
      .then(results => {
        batch.forEach((item, i) => item.resolve(results[i]));
      })
      .catch(error => {
        batch.forEach(item => item.reject(error));
      });
  }
}

// 使用:合并短时间内的多次请求
const userQueue = new RequestQueue(
  async (ids) => {
    const response = await fetch(`/api/users/batch`, {
      body: JSON.stringify(ids)
    });
    return response.json();
  },
  50 // 50ms内请求合并
);

// 多次调用会合并
userQueue.add(1);
userQueue.add(2);
userQueue.add(3); // 合并为一次请求 [1,2,3]

缓存策略设计

HTTP缓存

缓存类型特点适用场景
强缓存不发请求直接用缓存静态资源
协商缓存询问服务器是否可用动态数据
JavaScript
// 强缓存配置(服务端)
// Cache-Control: max-age=31536000, immutable
// 长期缓存静态资源

// 协商缓存配置
// ETag: "abc123"
// Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

// Webpack文件名hash
output: {
  filename: '[name].[contenthash:8].js' // 内容hash,更新则新文件
}

// HTML不缓存
Cache-Control: no-cache // 每次协商

本地存储缓存

JavaScript
// 内存缓存(最快)
const memoryCache = new Map();

function getWithMemory(key, fetcher) {
  if (memoryCache.has(key)) {
    return memoryCache.get(key);
  }
  const value = fetcher();
  memoryCache.set(key, value);
  return value;
}

// localStorage缓存
function getWithLocalStorage(key, fetcher, ttl = 3600000) {
  const cached = localStorage.getItem(key);
  if (cached) {
    const { value, timestamp } = JSON.parse(cached);
    if (Date.now() - timestamp < ttl) {
      return value;
    }
  }
  const value = await fetcher();
  localStorage.setItem(key, JSON.stringify({
    value,
    timestamp: Date.now()
  }));
  return value;
}

// IndexedDB缓存大数据
async function cacheLargeData(key, data) {
  const db = await openDB('cacheDB');
  await db.put('cache', { key, data, timestamp: Date.now() });
}

Service Worker缓存

JavaScript
// service-worker.js
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
  '/',
  '/styles.css',
  '/bundle.js'
];

// 安装时缓存静态资源
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(STATIC_ASSETS))
  );
});

// 请求拦截
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => {
        if (cached) return cached;

        return fetch(event.request)
          .then(response => {
            // 缓存新响应
            caches.open(CACHE_NAME)
              .then(cache => cache.put(event.request, response.clone()));
            return response;
          });
      })
  );
});

// 缓存策略:Cache First
async function cacheFirst(request) {
  const cached = await caches.match(request);
  return cached || fetch(request);
}

// 缓存策略:Network First
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    caches.open(CACHE_NAME).then(cache => cache.put(request, response.clone()));
    return response;
  } catch {
    return caches.match(request);
  }
}

// 缓存策略:Stale While Revalidate
async function staleWhileRevalidate(request) {
  const cached = await caches.match(request);

  const fetchPromise = fetch(request)
    .then(response => {
      caches.open(CACHE_NAME).then(cache => cache.put(request, response.clone()));
      return response;
    });

  return cached || fetchPromise;
}

多级缓存架构

缓存层级设计

JavaScript
class MultiLevelCache {
  constructor() {
    this.memoryCache = new Map(); // L1: 内存
    this.localStorageCache = new LocalStorageCache(); // L2: 本地存储
    this.serviceWorkerCache = new ServiceWorkerCache(); // L3: Service Worker
  }

  async get(key) {
    // L1检查
    if (this.memoryCache.has(key)) {
      return this.memoryCache.get(key);
    }

    // L2检查
    const localData = await this.localStorageCache.get(key);
    if (localData) {
      this.memoryCache.set(key, localData);
      return localData;
    }

    // L3检查
    const swData = await this.serviceWorkerCache.get(key);
    if (swData) {
      this.localStorageCache.set(key, swData);
      this.memoryCache.set(key, swData);
      return swData;
    }

    // 网络请求
    const networkData = await this.fetchNetwork(key);
    this.set(key, networkData);
    return networkData;
  }

  set(key, value) {
    this.memoryCache.set(key, value);
    this.localStorageCache.set(key, value);
    this.serviceWorkerCache.set(key, value);
  }

  invalidate(key) {
    this.memoryCache.delete(key);
    this.localStorageCache.delete(key);
    this.serviceWorkerCache.delete(key);
  }
}

数据缓存管理

JavaScript
// 带过期时间缓存
class TTLCache {
  constructor(defaultTTL = 60000) {
    this.cache = new Map();
    this.defaultTTL = defaultTTL;
  }

  set(key, value, ttl = this.defaultTTL) {
    this.cache.set(key, {
      value,
      expiresAt: Date.now() + ttl
    });

    // 清理过期
    this.cleanup();
  }

  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;

    if (Date.now() > item.expiresAt) {
      this.cache.delete(key);
      return null;
    }

    return item.value;
  }

  cleanup() {
    for (const [key, item] of this.cache) {
      if (Date.now() > item.expiresAt) {
        this.cache.delete(key);
      }
    }
  }
}

API缓存拦截

JavaScript
// Axios请求缓存拦截
const cache = new Map();

axios.interceptors.request.use(config => {
  if (config.method === 'get' && config.cache) {
    const cacheKey = `${config.url}${JSON.stringify(config.params)}`;
    if (cache.has(cacheKey)) {
      return Promise.reject({ cached: true, data: cache.get(cacheKey) });
    }
  }
  return config;
});

axios.interceptors.response.use(response => {
  if (response.config.method === 'get' && response.config.cache) {
    const cacheKey = `${response.config.url}${JSON.stringify(response.config.params)}`;
    cache.set(cacheKey, response.data);
  }
  return response;
}, error => {
  if (error.cached) {
    return Promise.resolve({ data: error.data });
  }
  return Promise.reject(error);
});

// 使用
axios.get('/api/data', { cache: true, ttl: 60000 });

缓存失效策略

主动失效

JavaScript
// 数据更新时清除缓存
async function updateUser(user) {
  const response = await fetch('/api/user', {
    method: 'PUT',
    body: JSON.stringify(user)
  });

  // 清除相关缓存
  cache.invalidate(`user-${user.id}`);
  cache.invalidate('user-list');

  return response;
}

版本号缓存

JavaScript
// 带版本号缓存
const CACHE_VERSION = 'v2';

function getCacheKey(key) {
  return `${CACHE_VERSION}:${key}`;
}

// 版本更新时自动失效旧缓存
localStorage.getItem('v1:user-1'); // 不会被新版本读取
localStorage.getItem('v2:user-1'); // 新版本数据

最大缓存限制

JavaScript
class SizeLimitedCache {
  constructor(maxSize = 100) {
    this.cache = new Map();
    this.maxSize = maxSize;
  }

  set(key, value) {
    if (this.cache.size >= this.maxSize) {
      // 删除最老的条目
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
}

要点总结

  1. 请求合并:资源合并、批量API、请求队列去抖
  2. 强缓存:Cache-Control配置静态资源长期缓存
  3. 协商缓存:ETag/Last-Modified动态数据校验
  4. 多级缓存:内存→LocalStorage→Service Worker→网络
  5. 缓存失效:主动清除、版本号、大小限制
  6. Service Worker:离线缓存、拦截请求、多策略切换

存放路径:articles/JS/专家/高级性能分析/请求合并与缓存策略.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 虚拟列表与大数据渲染
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库