ข้ามไปยังเนื้อหา

Cache API

เวลาต้องคุมเองว่าจะ cache request ไหน ใช้กลยุทธ์อะไร และเมื่อไรจะลบทิ้ง — service worker เพียงอย่างเดียวไม่พอ ต้องมี API ที่จัดการคู่ Request/Response ได้โดยตรง นี่คือหน้าที่ของ Cache API

Cache API เข้าถึงได้ผ่าน global object caches ด้านล่างนี้คือ method ทั้งหมดที่คุณจะใช้

เปิด cache ที่มีชื่อ หรือสร้างใหม่ถ้ายังไม่มี คืนค่าเป็น Cache object

const cache = await caches.open('demo-cache-v1');

จัดเก็บคู่ Request/Response อาร์กิวเมนต์แรกอาจเป็น Request object หรือ URL string ส่วนอาร์กิวเมนต์ที่สองต้องเป็น Response หากมี entry สำหรับ request เดิมอยู่แล้ว จะถูกแทนที่

const cache = await caches.open('demo-cache-v1');
await cache.put('/api/hello', new Response('{"status":"ok"}', {
headers: { 'Content-Type': 'application/json' },
}));

cache.add(url) ดึงข้อมูลจาก URL แล้วจัดเก็บ response ไว้ใน cache เป็น shorthand สำหรับ fetch(url) ตามด้วย cache.put(url, response)

cache.addAll(urls) ทำเช่นเดียวกันสำหรับ URL หลายรายการ แต่เป็นแบบ atomic: ถ้า request ใดล้มเหลว จะไม่มีการจัดเก็บใด ๆ เลย

const cache = await caches.open('demo-cache-v1');
// URL เดียว
await cache.add('/index.html');
// หลาย URL — ทั้งหมดหรือไม่มีเลย
await cache.addAll(['/index.html', '/styles/main.css', '/scripts/app.js']);

cache.match(request) ค้นหาใน cache เดียวและคืนค่า Response ที่ตรงกัน หรือ undefined ถ้าไม่พบ

caches.match(request) ค้นหาใน cache ทั้งหมด ของ origin และคืนค่า match แรกที่พบ

const cache = await caches.open('demo-cache-v1');
const response = await cache.match('/api/hello');
if (response) {
console.log(await response.json());
}

ลบ entry เฉพาะออกจาก cache ที่มีชื่อ คืนค่า true ถ้าพบและลบได้ หรือ false ถ้าไม่พบ

const cache = await caches.open('demo-cache-v1');
const deleted = await cache.delete('/api/hello');
console.log('Deleted:', deleted); // true หรือ false

คืนค่า array ของชื่อ cache ทั้งหมดสำหรับ origin ปัจจุบัน ใช้สำหรับจัดการ version

const names = await caches.keys();
console.log('Open caches:', names);
Browser Storage
ตัวเลือกBenefitCost
ใช้ cache.put() เก็บ opaque response (cross-origin, no-cors)ใช้งานได้กับ resource จาก CDN หรือ third-party ที่ไม่มี CORS headerนับ quota ที่ขนาดเต็มจริง แม้จะอ่าน body ไม่ได้ ทำให้ quota หมดเร็วโดยไม่รู้ตัว
ใช้ cache.addAll() แบบ atomic สำหรับ pre-cacheรับประกันว่า asset ที่ประกาศ install ครบทุกตัวหรือไม่มีเลย ป้องกัน state ที่ asset ขาดหายถ้า asset ตัวเดียว 404 การติดตั้งทั้งหมดล้มเหลว ต้องออกแบบ error handling ให้รอบคอบ
ตั้งชื่อ cache แบบมี version (เช่น demo-cache-v2)เปลี่ยน strategy หรืออัปเดต asset ได้โดยไม่ปนกับ cache เก่าต้องเขียนโค้ดลบ cache version เก่าเองใน activate event ไม่งั้น storage โตไม่มีที่สิ้นสุด
  • เก็บ opaque response ไว้ใน cache แล้วคิดว่าไม่กินพื้นที่เพราะอ่าน body ไม่ได้ — browser นับขนาดเต็มของ opaque response (มักหลาย MB ต่อรายการ) เข้า quota เหมือน response ปกติ
  • ไม่เคยลบ cache version เก่าเมื่อ deploy ใหม่ — ทุกครั้งที่เปลี่ยนชื่อ cache (เช่น v1 → v2) ต้องเรียก caches.delete() กับชื่อเก่าใน activate event ไม่งั้น storage สะสมไม่มีวันจบ
  • ใช้ cache.add()/addAll() กับ request โดยไม่เช็ค response status ก่อน — method เหล่านี้เก็บ response ทุกสถานะรวมถึง error page ที่ server อาจส่งมาโดยไม่ throw ต้องตรวจสอบเองถ้าต้องการความชัวร์

💡 ตัวอย่างจากของจริง

Wikipedia — offline reading mode ใช้ Cache API ร่วมกับ Service Worker เพื่อเก็บบทความและ asset ไว้ให้เปิดอ่านได้แม้ไม่มีเน็ต

Twitter Lite — ใช้ app-shell caching ผ่าน Cache API เก็บ HTML/CSS/JS หลักไว้ ทำให้เปิดแอปครั้งถัดไปโหลดเกือบทันที ก่อนค่อยดึงข้อมูลสดจาก network

cache.addAll([...urls]) ทำอะไรถ้า URL หนึ่งคืนค่า 404?
ความแตกต่างระหว่าง cache.match() และ caches.match() คืออะไร?
cache.delete(request) คืนค่าอะไร?
เมธอดใดคืนค่าชื่อ cache ทั้งหมดที่เปิดอยู่สำหรับ origin ปัจจุบัน?