ดูวิธีใช้ Cache API เพื่อให้ข้อมูลแอปพลิเคชันพร้อมใช้งานแบบออฟไลน์
Cache API เป็นระบบสำหรับจัดเก็บและเรียกข้อมูลคําขอเครือข่ายและการตอบกลับที่เกี่ยวข้อง ซึ่งอาจเป็นคําขอและคําตอบปกติที่สร้างในระหว่างการเรียกใช้แอปพลิเคชัน หรืออาจสร้างขึ้นเพื่อวัตถุประสงค์ในการเก็บข้อมูลไว้ใช้ภายหลังเท่านั้น
Cache API สร้างขึ้นเพื่อให้ Service Worker แคชคำขอเครือข่ายได้เพื่อให้สามารถตอบกลับได้อย่างรวดเร็ว ไม่ว่าจะมีความเร็วหรือความพร้อมของเครือข่ายเพียงใด อย่างไรก็ตาม คุณยังใช้ API นี้เป็นกลไกการจัดเก็บข้อมูลทั่วไปได้ด้วย
พร้อมให้บริการที่ไหนบ้าง
Cache API พร้อมใช้งานในเบราว์เซอร์สมัยใหม่ทั้งหมด โดย API นี้จะแสดงผ่านพร็อพเพอร์ตี้ caches
ทั่วโลก คุณจึงทดสอบการมีอยู่ของ API ได้ด้วยการตรวจหาฟีเจอร์ง่ายๆ ดังนี้
const cacheAvailable = 'caches' in self;
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()
แต่ใช้อาร์เรย์ของออบเจ็กต์ Request
หรือ URL (string
) ซึ่งทํางานคล้ายกับการเรียก cache.add
สําหรับคําขอแต่ละรายการ ยกเว้นว่า Promise
จะปฏิเสธหากไม่มีการแคชคําขอใดก็ตาม
const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);
ในแต่ละกรณีเหล่านี้ รายการใหม่จะเขียนทับรายการที่มีอยู่ซึ่งตรงกัน ซึ่งจะใช้กฎการจับคู่เดียวกันกับที่อธิบายไว้ในส่วนการดึงข้อมูล
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
มาแล้วและต้องการเข้าถึงเนื้อหา คุณสามารถใช้เมธอดตัวช่วยได้หลายวิธี โดยแต่ละรายการจะแสดงผล 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
ในกรณีอื่นๆ
เบราว์เซอร์ใช้ข้อมูลมากกว่าแค่ URL เพื่อพิจารณาว่า Requests
2 รายการตรงกันหรือไม่ ระบบจะถือว่าคำขอ 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()
สำหรับแต่ละแคช
กำลังค้นหา
Cache 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