ปัจจุบันมีเพียงการพูดถึงและข้อมูลโค้ดสั้นๆ ของอินเทอร์เฟซ Cache
หากต้องการใช้โปรแกรมทำงานของบริการอย่างมีประสิทธ���ภาพ คุณจำเป็นต้องใช้กลยุทธ์การแคชอย่างน้อย 1 อย่างซึ่งจะต้องทำความคุ้นเคยกับอินเทอร์เฟซของ Cache
เล็กน้อย
กลยุทธ์การแคชคือการโต้ตอบระหว่างเหตุการณ์ fetch
ของ Service Worker และอินเทอร์เฟซ Cache
วิธีการเขียนการแคชจะขึ้นอยู่กับวิธีการ
เช่น การจัดการคำขอเนื้อหาแบบคงที่ให้แตกต่างจากเอกสาร และวิธีนี้จะส่งผลต่อการสร้างกลยุทธ์การแคช
ก่อนที่จะเข้าเรื่องกลยุทธ์ เรามาใช้เวลาสักครู่เพื่อพูดคุยว่าCache
อินเทอร์เฟซคืออะไรและคืออะไร
และสรุปวิธีการบางส่วนที่ใช้จัดการแคชของ Service Worker
อินเทอร์เฟซ Cache
กับแคช HTTP
หากคุณไม่เคยใช้อินเทอร์เฟซ Cache
มาก่อน คุณอาจคิดว่าเป็นอินเทอร์เฟซเดียวกันหรืออย่างน้อยก็มีความเกี่ยวข้องกับแคช HTTP ซึ่งจริงๆ แล้วไม่ได้เป็นเช่นนั้น
- อินเทอร์เฟซ
Cache
เป็นกลไกการแคชที่แยกจากแคช HTTP โดยสิ้นเชิง - การกำหนดค่า
Cache-Control
ใดๆ ที่คุณใช้เพื่อให้ส่งผลต่อแคช HTTP จะไม่มีผลต่อเนื้อหาที่ได้รับการจัดเก็บในอินเทอร์เฟซCache
คุณควรมองว่าแคชของเบราว์เซอร์นั้นซ้อนกันเป็นชั้นๆ ด้วย แคช HTTP คือแคชระดับต่ำที่ขับเคลื่อนโดยคู่คีย์-ค่าพร้อมด้วยคำสั่งที่แสดงในส่วนหัว HTTP
ในทางตรงกันข้าม อินเทอร์เฟซ Cache
เป็นแคชระดับสูงที่ขับเคลื่อนโดย JavaScript API
ซึ่งให้ความยืดหยุ่นมากกว่าการใช้คู่คีย์-ค่า HTTP ที่ค่อนข้างง่าย และยังเป็นครึ่งหนึ่งของสิ่งที่ทำให้กลยุทธ์การแคชทำได้
เมธอด API ที่สำคัญบางส่วนเกี่ยวกับแคชของ Service Worker มีดังนี้
CacheStorage.open
เพื่อสร้างอินสแตนซ์Cache
ใหม่Cache.add
และCache.put
เพื่อจัดเก็บคำตอบของเครือข่ายไว้ในแคชของ Service WorkerCache.match
เพื่อค้นหาก��รตอบกลับที่แคชไว้ในอินสแตนซ์Cache
Cache.delete
เพื่อนำการตอบกลับที่แคชไว้ออ����า����ินสแตนซ์Cache
นี่เป็นตัวอย่างเพียงเล็กน้อย ยังมีวิธีการอื่นๆ ที่มีประโยชน์อีกมากมาย แต่นี่เป็นวิธีพื้นฐานที่คุณจะเห็นว่าใช้กันในภายหลังในคู่มือนี้
กิจกรรม fetch
ที่เรียบง่าย
กลยุทธ์การแคชอีกครึ่งหนึ่งคือเหตุการณ์ fetch
ของ Service Worker
ถึงตอนนี้ คุณคงได้ยินเกี่ยวกับ "การสกัดกั้นคำขอของเครือข่าย" มาบ้างในเอกสารนี้ และเหตุการณ์ fetch
ภายใน Service Worker ก็เกิดขึ้น ดังนี้
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('install', (event) => {
event.waitUntil(caches.open(cacheName));
});
self.addEventListener('fetch', async (event) => {
// Is this a request for an image?
if (event.request.destination === 'image') {
// Open the cache
event.respondWith(caches.open(cacheName).then((cache) => {
// Respond with the image from the cache or from the network
return cache.match(event.request).then((cachedResponse) => {
return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
// Add the network response to the cache for future visits.
// Note: we need to make a copy of the response to save it in
// the cache and use the original as the request response.
cache.put(event.request, fetchedResponse.clone());
// Return the network response
return fetchedResponse;
});
});
}));
} else {
return;
}
});
นี่คือตัวอย่างของเล่นและตัวอย่างการทำงานจริงที่คุณสามารถดูได้ แต่เป็นตัวอย่างที่แสดงให้เห็นคร่าวๆ ว่าโปรแกรมทำงานของบริการทำอะไรได้บ้าง โค้ดด้านบนทำสิ่งต่อไปนี้
- ตรวจสอบพร็อพเพอร์ตี้
destination
ของคําขอเพื่อดูว่าเป็นคําขอรูปภาพหรือไม่ - หากอิมเมจอยู่ในแคชของ Service Worker ให้แสดงผลจากที่นั่น หากไม่ ให้ดึงข้อมูลรูปภาพจากเครือข่าย แล้วเก็บการตอบกลับไว้ในแคช แล้ว���่งการตอบกลับของเครือข่าย
- คำขออื่นๆ ทั้งหมดจะส่งผ่าน Service Worker โดยไม่มีการโต้ตอบกับแคช
ออบเจ็กต์ event
ของการดึงข้อมูลมี
พร็อพเพอร์ตี้ request
ซึ่งมีข้อมูลที่เป็นประโยชน์บางส่วนที่จะช่วยคุณระบุประเภทของคำขอแต่ละรายการ ดังนี้
url
ซึ่งเป็น URL สำหรับคำขอเครือข่ายที่กำลังได้รับการจัดการโดยเหตุการณ์fetch
method
ซึ่งเป็นเมธอดคำขอ (เช่นGET
หรือPOST
)mode
ซึ่งอธิบายโหมดของคำขอ ค่า'navigate'
มักใช้เพื่อแยกคำขอสำหรับเอกสาร HTML ออกจากคำขออื่นๆdestination
ซึ่งอธิบายประเภทเนื้อหาที่ขอเพื่อไม่ให้ใช้นามสกุลไฟล์ของเนื้อหาที่ขอ
ขอย้ำอีกครั้งว่าชื่อเกมไม่พร้อมกัน
คุณจะทราบว่าเหตุการณ์ install
เสนอเมธอด event.waitUntil
ซึ่งคงไว้ตามที่สัญญาไว้ และรอให้แก้ไขปัญหาเสร็จก่อนจึงดำเนินการต่อ
เหตุการณ์ fetch
มีเมธอด event.respondWith
ที่คล้ายกันซึ่งคุณใช้ส่งกลับผลลัพธ์ของคำขอ fetch
แบบไม่พร้อมกัน หรือการตอบกลับโดยเมธอด match
ของอินเทอร์เฟซ Cache
ได้
กลยุทธ์การแคช
เมื่อคุณทำความคุ้นเคยกับอินสแตนซ์ Cache
และเครื่องจัดการเหตุการณ์ fetch
แล้ว คุณก็พร้อมที่จะเจาะลึกกลยุทธ์การแคชโปรแกรมทำงานบางส่วนแล้ว
แม้ว่าความเป็นไปได้นั้นจะไม่มีที่สิ้นสุด แต่คู่มือนี้จะยึดตามกลยุทธ์ที่มาพร้อมกับ Workbox เพื่อให้คุณเห็นภาพความเป็นไปภายในของ Workbox
แคชเท่านั้น
เรามาเริ่มจากกลยุทธ์การแคชง่ายๆ ที่เราเรียกว่า "แคชเท่านั้น" กล่าวคือ เมื่อ Service Worker เป็นผู้ควบคุมหน้าเว็บ คำขอที่ตรงกันจะไปยังแคชเท่านั้น ซึ่งหมายความว่าเนื้อหาที่แคชไว้ทั้งหมดจะต้องได้รับการแคชล่วงหน้าเพื่อให้สามารถใช้รูปแบบได้ และเนื้อหาเหล่านั้นจะไม่ได้รับการอัปเดตในแคชจนกว่าจะอัปเดต Service Worker
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
// Assets to precache
const precachedAssets = [
'/possum1.jpg',
'/possum2.jpg',
'/possum3.jpg',
'/possum4.jpg'
];
self.addEventListener('install', (event) => {
// Precache assets on install
event.waitUntil(caches.open(cacheName).then((cache) => {
return cache.addAll(precachedAssets);
}));
});
self.addEventListener('fetch', (event) => {
// Is this one of our precached assets?
const url = new URL(event.request.url);
const isPrecachedRequest = precachedAssets.includes(url.pathname);
if (isPrecachedRequest) {
// Grab the precached asset from the cache
event.respondWith(caches.open(cacheName).then((cache) => {
return cache.match(event.request.url);
}));
} else {
// Go to the network
return;
}
});
ด้านบน อาร์เรย์ของเนื้อหาจะถูกแคชล่วงหน้าไว้ ณ เวลาที่ติดตั้ง
เมื่อโปรแกรมทำงานของบริการจัดการการดึงข้อมูล เราจะตรวจสอบว่า URL คำขอที่จัดการโดยเหตุการณ์ fetch
อยู่ในอาร์เรย์ของเนื้อหาที่จัดเก็บล่วงหน้าหรือไม่
หากเป็นเช่นนั้น เราจะดึงทรัพยากรมาจากแคช แล้วข้ามเครือข่ายไป
ขณะที่คำขออื่นๆ จะผ่านไปยัง
เครือข่ายและมีเพียงเครือข่าย
หากต้องการดูการทำงานของกลยุทธ์นี้
ให้ดูการสาธิตนี้ขณะที่คอนโซลเปิดอยู่
เครือข่ายเท่านั้น
ซึ่งจะตรงข้ามกับ "แคชเท่านั้น" คือ "เครือข่ายเท่านั้น" ซึ่งจะส่งคำขอผ่าน Service Worker ไปยังเครือข่ายโดยไม่มีการโต้ตอบกับแคชของ Service Worker นี่เป็นกลยุทธ์ที่ดีในการดูแลความสดใหม่ข��งเนื้อหา (นึกถึงมาร์กอัป) แต่ข้อเสียก็คือ วิธีนี้จะไม่ทำงานเมื่อผู้ใช้ออฟไลน์
การดูแลให้คำขอส่งผ่านไปยังเครือข่ายหมายความว่าคุณไม่ได้เรียกใช้ event.respondWith
สำหรับคำขอจับคู่
ถ้าต้องการระบุให้ชัดเจน ให้ใช้ return;
ที่ว่างเปล่าในโค้ดเรียกกลับของเหตุการณ์ fetch
สำหรับคำขอที่ต้องการส่งผ่านไปยังเครือข่าย
นี่คือสิ่งที่เกิดขึ้นในการสาธิตกลยุทธ์ "แคชเท่านั้น" สำหรับคำขอที่ไม่ได้แคชล่วงหน้า
แคชก่อน กำลังกลับไปใช้เครือข่าย
กลยุทธ์นี้จะช่วยจัดการสิ่งต่างๆ ให้เข้ามาเกี่ยวข้องมากขึ้น สำหรับคำขอที่ตรงกัน กระบวนการจะเป็นดังนี้
- คำขอเข้าสู่แคช หากเนื้อหาอยู่ในแคช ให้แสดงเนื้อหาดังกล่าว
- หากคำขอไม่อยู่ในแคช ให้ไปที่เครือข่าย
- เมื่อคำขอเครือข่ายเสร็จสิ้น ให้เพิ่มคำขอไปยังแคช แล้วแสดงการตอบสนองจากเครือข่าย
นี่คือตัวอย่างของกลยุทธ์นี้ซึ่งคุณทดสอบได้ในการสาธิตการใช้งานจริง
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('fetch', (event) => {
// Check if this is a request for an image
if (event.request.destination === 'image') {
event.respondWith(caches.open(cacheName).then((cache) => {
// Go to the cache first
return cache.match(event.request.url).then((cachedResponse) => {
// Return a cached response if we have one
if (cachedResponse) {
return cachedResponse;
}
// Otherwise, hit the network
return fetch(event.request).then((fetchedResponse) => {
// Add the network response to the cache for later visits
cache.put(event.request, fetchedResponse.clone());
// Return the network response
return fetchedResponse;
});
});
}));
} else {
return;
}
});
แม้ว่าตัวอย่างนี้จะครอบคลุมเฉพาะรูปภาพเท่านั้น นี่เป็นกลยุทธ์ที่ยอดเยี่ยมที่จะใช้กับเนื้อหาแบบคงที่ทั้งหมด (เช่น CSS, JavaScript, รูปภาพ และแบบอักษร) โดยเฉพาะเนื้อหาที่มีการกำหนดแฮช โหมดนี้จะเพิ่มประสิทธิภาพความเร็วของเนื้อหาที่เปลี่ยนแปลงไม่ได้ โดยตรวจสอบความใหม่ของเนื้อหาควบคู่กับเซิร์ฟเวอร์ที่แคช HTTP อาจเริ่มขึ้น และที่ส��คัญกว่านั้น เนื้อหาที่แคชทั้งหมดจะใช้งานแบบออฟไลน์ได้
เครือข่ายก่อน กลับไปใช้แคช
หากคุณพลิก "แคชก่อน เครือข่ายที่สอง" บนศีรษะ คุณก็จะได้กลยุทธ์ "เครือข่ายแรก แคชที่สอง" ซึ่งมีลักษณะดังนี้
- คุณจะต้องไปที่เครือข่ายก่อนเพื่อดำเนินการตามคำขอ แล้ววางการตอบกลับไว้ในแคช
- ถ้าคุณออฟไลน์ในภายหลัง คุณจะกลับไปใช้การตอบกลับเวอร์ชันล่าสุดในแคช
กลยุทธ์นี้เหมาะอย่างยิ่งสำหรับคำขอ HTML หรือ API ขณะที่ คุณต้องการทรัพยากรเวอร์ชันล่าสุด แต่ยังต้องการให้สิทธิ์เข้าถึงแบบออฟไลน์แก่เวอร์ชันล่าสุดที่พร้อมใช้งาน ต่อไปนี้คือลักษณะของคำขอเมื่อใช้คำขอ HTML
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('fetch', (event) => {
// Check if this is a navigation request
if (event.request.mode === 'navigate') {
// Open the cache
event.respondWith(caches.open(cacheName).then((cache) => {
// Go to the network first
return fetch(event.request.url).then((fetchedResponse) => {
cache.put(event.request, fetchedResponse.clone());
return fetchedResponse;
}).catch(() => {
// If the network is unavailable, get
return cache.match(event.request.url);
});
}));
} else {
return;
}
});
คุณทดลองใช้ได้ในการสาธิต ก่อนอื่นให้ไปที่หน้า คุณอาจต้องโหลดซ้ำก่อนที่การตอบกลับ HTML จะปรากฏในแคช จากนั้นจำลองการเชื่อมต่อแบบออฟไลน์ในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ แล้วโหลดซ้ำอีกครั้ง ระบบจะแสดงผลเวอร์ชันล่าสุดจากแคชทันที
ในกรณีที่ความสามารถในการทำงานแบบออฟไลน์เป็นสิ่งสำคัญ แต่คุณต้องรักษาสมดุลระหว่างความสามารถนั้นกับการเข้าถึงมาร์กอัปหรือข้อมูล API เวอร์ชันล่าสุด "เครือข่ายต้องมาก่อน แคชที่สอง" เป็นกลยุทธ์ที่แข็งแกร่งซึ่งช่วยบรรลุเป้าหมายนั้น
ไม่อัปเดตขณะตรวจสอบใหม่
ในบรรดากลยุทธ์ที่เราได้พูดถึงก่อนหน้านี้ "ไม่อัปเดตขณะตรวจสอบใหม่" มีความซับซ้อนมากที่สุด วิธีนี้คล้ายกับกลยุทธ์ 2 ข้อสุดท้าย แต่กระบวนการจะให้ความสำคัญกับความเร็วในการเข้าถึงแหล่งข้อมูล ในขณะเดียวกันก็ต้องอัปเดตแหล่งข้อมูลในเบื้องหลัง กลยุทธ์นี้มีลักษณะดังนี้
- ในคำขอแรกสำหรับเนื้อหา ให้ดึงข้อมูลจากเครือข่าย ใส่ไว้ในแคช และส่งการตอบกลับของเครือข่าย
- สำหรับคำขอในลำดับถัดมา ให้แสดงเนื้อหาจากแคชก่อน จากนั้นแสดง "ในเบื้องหลัง" ส่งคำขอจากเครือข่ายอีกครั้งและอัปเดตรายการแคชของเนื้อหา
- สำหรับคำขอหลังจากนั้น คุณจะได้รับเวอร์ชันล่าสุดที่ดึงจากเครือข่ายที่อยู่ในแคชในขั้นตอนก่อนหน้า
นี่เป็นกลยุทธ์ที่ยอดเยี่ยมสำหรับสิ่งต่างๆ ที่มีความสำคัญในการติดตามข้อมูลล่าสุด แต่ไม่ได้สำคัญมาก ลองนึกถึงสิ่งต่างๆ อย่างเช่น รูปโปรไฟล์สำหรับเว็บไซต์โซเชียลมีเดีย คำขอเหล่านี้จะได้รับการอัปเดตเมื่อผู้ใช้สามารถทำเช่นนั้นได้ แต่เวอร์ชันล่าสุดไม่จำเป็นต้องเป็นสิ่งจำเป็นเสมอไปในทุกคำขอ
// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'image') {
event.respondWith(caches.open(cacheName).then((cache) => {
return cache.match(event.request).then((cachedResponse) => {
const fetchedResponse = fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchedResponse;
});
}));
} else {
return;
}
});
คุณสามารถดูการทำงานจริงได้ในการสาธิตอื่นที่ใช้จริง โดยเฉพาะอย่างยิ่งถ้าคุณดูที่แท็บเครือข่ายในเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของเบราว์เซอร์และแว่น CacheStorage
(หากเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของเบราว์เซอร์มีเครื่องมือดังกล่าว)
ไปยังเวิร์กบ็อกซ์ได้เลย
เอกสารนี้จะสรุปการตรวจสอบ API ของ Service Worker รวมถึง API ที่เกี่ยวข้อง ซึ่งหมายความว่าคุณได้เรียนรู้มากเพียงพอเ��ี่ยวกับวิธีใช้ Service Worker โดยตรงเพื่อเริ่มปรับแต่ง Workbox!