Cache API: คู่มือฉบับย่อ

เรียนรู้วิธีใช้ API แคชเพื่อทำให้ข้อมูลแอปพลิเคชันของคุณใช้งานแบบออฟไลน์ได้

Cache API คือระบบสำหรับจัดเก็บและเรียกคำขอเครือข่ายและการตอบสนองที่เกี่ยวข้อง คำขอเหล่านี้อาจเป็นคำขอและคำตอบทั่วไปที่สร้างขึ้นในระหว่างการใช้งานแอปพลิเคชัน หรือสร้างขึ้นเพื่อจุดประสงค์ในการจัดเก็บข้อมูลไว้ใช้ในภายหลังเท่านั้น

API แคชสร้างขึ้นเพื่อให้ Service Worker แคชคำขอเครือข่ายเพื่อให้ตอบสนองได้อย่างรวดเร็ว โดยไม่คำนึงถึงความเร็วหรือความพร้อมใช้งานของเครือข่าย อย่างไรก็ตาม API ยังสามารถใช้เป็นกลไกการจัดเก็บข้อมูลทั่วไปได้

พร้อมให้บริการที่ไหนบ้าง

API แคชมีอยู่ในเบราว์เซอร์รุ่นใหม่ทั้งหมด จะแสดงผ่านพร็อพเพอร์ตี้ caches ส่วนกลางเพื่อให้คุณทดสอบการมีอยู่ของ API ได้ด้วยการตรวจหาฟีเจอร์แบบง่ายๆ

const cacheAvailable = 'caches' in self;

การรองรับเบราว์เซอร์

  • 40
  • 16
  • 41
  • 11.1

แหล่งที่มา

คุณสามารถเข้าถึง Cache API ได้จากหน้าต่าง, iframe, Worker หรือ Service Worker

ข้อมูลที่จัดเก็บได้

แคชจะจัดเก็บเฉพาะออบเจ็กต์ Request และ Response คู่กันเท่านั้น โดยจะแสดงคำขอ HTTP และการตอบกลับตามลำดับ แต่คำขอและการตอบกลับอาจมีข้อมูลประเภทใดก็ได้ที่โอนผ่าน HTTP ได้

คุณจะจัดเก็บได้มากแค่ไหน

กล่าวโดยสรุปคือมาก อย่างน้อย 2-3 แสนเมกะไบต์ และอาจรวมถึงหลายร้อยกิกะไบต์ขึ้นไป การใช้งานเบราว์เซอร์จะแตกต่างกันไป แต่ปริมาณพื้นที่เก็บข้อมูลที่ใช้ได้มักจะขึ้นอยู่กับปริมาณพื้นที่เก็บข้อมูลที่มีในอุปกรณ์

การสร้างและเปิดแคช

หากต้องการเปิดแคช ให้ใช้เมธอด caches.open(name) โดยส่งชื่อของแคชเป็นพารามิเตอร์เดี่ยว หากไม่มีแคชที่มีชื่ออยู่ ระบบจะสร้างแคชดังกล่าวขึ้น เมธอดนี้จะแสดง Promise ที่แก้ไขด้วยออบเจ็กต์ Cache

const cache = await caches.open('my-cache');
// do something with cache...

กำลังเพิ่มลงในแคช

การเพิ่มรายการไปยังแคชทำได้ 3 วิธี ได้แก่ add, addAll และ put ทั้ง 3 วิธีจะแสดงผล Promise

cache.add

รายการแรกคือ cache.add() โดยใช้พารามิเตอร์ 1 รายการ ซึ่งอาจเป็น Request หรือ URL (string) และส่งคำขอไปยังเครือข่ายและจัดเก็บการตอบกลับไว้ในแคช หากการดึงข้อมูลไม่สำเร็จหรือรหัสสถานะของการตอบกลับไม่ได้อยู่ในช่วง 200 ระบบก็จะไม่เก็บข้อมูลไว้และ Promise จะปฏิเสธ โปรดทราบว่าระบบจัดเก็บคำขอข้ามต้นทางที่ไม่ได้อยู่ในโหมด CORS ไม่ได้ เนื่องจากคำขอดังกล่าวแสดงผล status เป็น 0 คำขอดังกล่าวจะจัดเก็บไว้กับ put ได้เท่านั้น

// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');

cache.addAll

ต่อไปเป็นข่าว cache.addAll() ซึ่งทำงานคล้ายกับ add() แต่จะรับอาร์เรย์ของออบเจ็กต์หรือ URL Request รายการ (string) ซึ่งทำงานคล้ายกับการเรียกใช้ cache.add สำหรับคำขอแต่ละรายการ ยกเว้นว่า Promise จะปฏิเสธหากไม่มีแคชคำขอเดียว

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

ในแต่ละกรณีเหล่านี้ รายการใหม่จะเขียนทับรายการที่มีอยู่ที่ตรงกัน ซึ่งจะใช้กฎการจับคู่เดียวกันกับที่อธิบายไว้ในส่วนretrieving

cache.put

สุดท้ายคือ cache.put() ที่ให้คุณจัดเก็บการตอบสนองจากเครือข่าย หรือสร้างและจัดเก็บ Response ของคุณเอง ต้องใช้ 2 พารามิเตอร์ รายการแรกอาจเป็นออบเจ็กต์ Request หรือ URL (string) รายการที่ 2 ต้องเป็น Response ซึ่งมาจากเครือข่ายหรือสร้างขึ้นจากโค้ดของคุณ

// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');

เมธอด put() ให้สิทธิ์มากกว่า add() หรือ addAll() และจะช่วยให้คุณจัดเก็บการตอบกลับที่ไม่ใช่ CORS หรือการตอบกลับอื่นๆ ที่รหัสสถานะของการตอบกลับไม่ได้อยู่ในช่วง 200 ซึ่งจะเขียนทับคำตอบก่อนหน้านี้ ของคำขอเดียวกัน

การสร้างออบเจ็กต์คำขอ

สร้างออบเจ็กต์ Request โดยใช้ URL สำหรับไฟล์ที่กำลังจัดเก็บ

const request = new Request('/my-data-store/item-id');

การทำงานกับออบเจ็กต์การตอบกลับ

เครื่องมือสร้างออบเจ็กต์ Response รับข้อมูลหลายประเภท รวมถึงออบเจ็กต์ Blob, ArrayBuffer, FormData และสตริง

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

คุณสามารถตั้งค่าประเภท MIME ของ Response ได้โดยการตั้งค่าส่วนหัวที่เหมาะสม

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

หากเรียกข้อมูล Response แล้วและต้องการเข้าถึงเนื้อหาของ Response ก็มีหลายวิธีที่คุณสามารถใช้ได้ แต่ละรายการจะแสดง Promise ที่แก้ด้วยค่าประเภทอื่น

วิธีการ คำอธิบาย
arrayBuffer แสดงผล ArrayBuffer ที่มีเนื้อหาซึ่งแปลงเป็นจำนวนไบต์
blob แสดงผล Blob หากสร้าง Response ด้วย Blob แสดงว่า Blob ใหม่นี้จะเป็นประเภทเดียวกัน มิเช่นนั้นระบบจะใช้ Content-Type ของ Response
text ตีความไบต์ของเนื้อความเป็นสตริงที่เข้ารหัสแบบ UTF-8
json ตีความไบต์ของเนื้อความเป็นสตริงที่เข้ารหัสแบบ UTF-8 แล้วพยายามแยกวิเคราะห์เป็น JSON แสดงผลออบเจ็กต์ที่ได้ หรือแสดง TypeError หากแยกวิเคราะห์สตริงเป็น JSON ไม่ได้
formData ตีความไบต์ของเนื้อความเป็นรูปแบบ HTML ซึ่งเข้ารหัสเป็น multipart/form-data หรือ application/x-www-form-urlencoded แสดงผลออบเจ็กต์ FormData หรือส่ง TypeError หากแยกวิเคราะห์ข้อมูลไม่ได้
body แสดงผล ReadableStream สําหรับข้อมูลร่างกาย

เช่น

const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

กำลังดึงข้อมูลจากแคช

หากต้องการค้นหารายการในแคช คุณสามารถใช้เมธอด match ได้

const response = await cache.match(request);
console.log(request, response);

หาก request เป็นสตริง เบราว์เซอร์จะแปลงเป็น Request โดยเรียกใช้ new Request(request) ฟังก์ชันจะแสดงผล Promise ที่แปลงเป็น Response หากพบรายการที่ตรงกัน หรือแสดง undefined ในกรณีอื่นๆ

ในการพิจารณาว่า Requests 2 รายการตรงกันหรือไม่ เบราว์เซอร์จะใช้มากกว่าแค่ URL ระบบจะถือว่าคำขอ 2 รายการแตกต่างกันหากมีสตริงการค้นหา ส่วนหัว Vary หรือเมธอด HTTP ที่แตกต่างกัน (GET, POST, PUT ฯลฯ)

คุณอาจเพิกเฉยต่อสิ่งเหล่านี้หรือทั้งหมดก็ได้โดยการส่งออบเจ็กต์ตัวเลือกเป็นพารามิเตอร์ที่ 2

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

หากคำขอที่แคชไว้มากกว่า 1 รายการตรงกัน ระบบจะแสดงผลคำขอที่สร้างขึ้นก่อน หากต้องการดึงคำตอบที่ตรงกันทั้งหมด คุณก็ใช้ cache.matchAll() ได้

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);

คุณจะค้นหาแคชทั้งหมดพร้อมกันได้โดยใช้ caches.match() เป็นทางลัด คุณจึงค้นหาแคชแต่ละรายการแทนการเรียก cache.match() ได้

กำลังค้นหา

API แคชไม่ได้ให้วิธีค้นหาคำขอหรือการตอบกลับ ยกเว้นการจับคู่รายการกับออบเจ็กต์ Response แต่คุณสามารถใช้การค้นหาของคุณเองโดยใช้การกรองหรือโดยการสร้างดัชนีได้

การกรอง

วิธีหนึ่งในการค้นหาของคุณเองคือการตรวจสอบรายการทั้งหมดซ้ำและกรองรายการที่คุณต้องการ สมมติว่าคุณต้องการค้นหารายการทั้งหมด ที่มี URL ที่ลงท้ายด้วย .png

async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}

ด้วยวิธีนี้ คุณจะใช้พร็อพเพอร์ตี้ใดก็ได้ของออบเจ็กต์ Request และ Response เพื่อกรองรายการ โปรดทราบว่าการดำเนินการนี้จะช้าหากคุณค้นหาข้อมูลในชุดข้อมูลขนาดใหญ่

การสร้างดัชนี

อีกวิธีหนึ่งในการใช้การค้นหาของคุณเองคือการรักษาดัชนีแยกต่างหากของรายการที่สามารถค้นหาและจัดเก็บดัชนีไว้ใน IndexedDB เนื่องจากการดำเนินการประเภทนี้เป็นการดำเนินการที่ IndexedDB ออกแบบมาเพื่อให้คำสั่งนี้มีประสิทธิภาพดีกว่ามากเมื่อมีรายการจำนวนมาก

หากจัดเก็บ URL ของ Request ไว้ข้างๆ พร็อพเพอร์ตี้ที่ค้นหาได้ คุณจะเรียกข้อมูลรายการแคชที่ถูกต้องได้อย่างง่ายดายหลังจากทำการค้นหา

การลบรายการ

หากต้องการลบรายการออกจากแคช ให้ทำดังนี้

cache.delete(request);

ตำแหน่งที่คำขออาจเป็น Request หรือสตริง URL วิธีนี้ยังใช้ออบเจ็กต์ตัวเลือกเดียวกับ cache.match ซึ่งช่วยให้คุณลบคู่ Request/Response หลายคู่สำหรับ URL เดียวกันได้

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

การลบแคช

หากต้องการลบแคช โปรดโทรหา caches.delete(name) ฟังก์ชันนี้แสดงผล Promise ที่แก้ไขเป็น true หากมีแคชอยู่และถูกลบไปแล้ว หรือหากเป็น false

ขอขอบคุณ

ต้องขอขอบคุณ Mat Scales ที่เขียนบทความเวอร์ชันต้นฉบับนี้ ซึ่งได้ปรากฏตัวเป็นครั้งแรกใน WebFundamentals