Cache API
เวลาต้องคุมเองว่าจะ cache request ไหน ใช้กลยุทธ์อะไร และเมื่อไรจะลบทิ้ง — service worker เพียงอย่างเดียวไม่พอ ต้องมี API ที่จัดการคู่ Request/Response ได้โดยตรง นี่คือหน้าที่ของ Cache API
method ทั้งหมดของ Cache API
หัวข้อที่มีชื่อว่า “method ทั้งหมดของ Cache API”Cache API เข้าถึงได้ผ่าน global object caches ด้านล่างนี้คือ method ทั้งหมดที่คุณจะใช้
caches.open(name)
หัวข้อที่มีชื่อว่า “caches.open(name)”เปิด cache ที่มีชื่อ หรือสร้างใหม่ถ้ายังไม่มี คืนค่าเป็น Cache object
const cache = await caches.open('demo-cache-v1');cache.put(request, response)
หัวข้อที่มีชื่อว่า “cache.put(request, response)”จัดเก็บคู่ 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) และ cache.addAll(urls)
หัวข้อที่มีชื่อว่า “cache.add(url) และ cache.addAll(urls)”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) และ caches.match(request)
หัวข้อที่มีชื่อว่า “cache.match(request) และ caches.match(request)”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());}cache.delete(request)
หัวข้อที่มีชื่อว่า “cache.delete(request)”ลบ entry เฉพาะออกจาก cache ที่มีชื่อ คืนค่า true ถ้าพบและลบได้ หรือ false ถ้าไม่พบ
const cache = await caches.open('demo-cache-v1');const deleted = await cache.delete('/api/hello');console.log('Deleted:', deleted); // true หรือ falsecaches.keys()
หัวข้อที่มีชื่อว่า “caches.keys()”คืนค่า array ของชื่อ cache ทั้งหมดสำหรับ origin ปัจจุบัน ใช้สำหรับจัดการ version
const names = await caches.keys();console.log('Open caches:', names);ลองเลย: สร้าง, จับคู่, และล้าง cache
หัวข้อที่มีชื่อว่า “ลองเลย: สร้าง, จับคู่, และล้าง cache”ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
ใช้ 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()กับชื่อเก่าในactivateevent ไม่งั้น 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