จนถึงตอนนี้ มีเพียงการพูดถึงและข้อมูลโค้ดสั้นๆ ของ
อินเทอร์เฟซ Cache
หากต้องการใช้โปรแกรมทำงานของบริการอย่างมีประสิทธิภาพ คุณต้องใช้กลยุทธ์การแคชอย่างน้อย 1 กลยุทธ์
ซึ่งต้องใช้ความคุ้นเคยกับอินเทอร์เฟซของ Cache
เล็กน้อย
กลยุทธ์การแคชคือการโต้ตอบระหว่างเหตุการณ์ fetch
ของโปรแกรมทำงานของบริการกับอินเทอร์เฟซ 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
เหตุการณ์
ที่ผ่านมาในเอกสารนี้ คุณได้ได้ยินเกี่ยวกับ "การสกัดกั้นคำขอเครือข่าย" มาบ้างแล้ว
และเหตุการณ์ 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
หรือการตอบกลับจากอินเทอร์เฟซ Cache
match
วิธี
กลยุทธ์การแคช
เมื่อคุณเริ่มคุ้นเคยกับอินสแตนซ์ 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;
ที่ว่างเปล่าใน Callback ของเหตุการณ์ 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 ยิ่งไปกว่านั้น เนื้อหาที่แคชไว้ทั้งหมดจะใช้งานได้แบบออฟไลน์
เครือข่ายทำงานก่อน โดยกลับไปใช้แคช
หากคุณพลิก "แคชก่อน เครือข่ายที่ 2" บนหัว คุณจะได้รับข้อความว่า "เครือข่ายก่อน แคชวินาที" ซึ่งมีหน้าตาเป็นอย่างไร
- คุณต้องไปที่เครือข่ายก่อนเพื่อขอคำขอ แล้ววางคำตอบไว้ในแคช
- หากคุณออฟไลน์ในภายหลัง คุณจะกลับไปใช้การตอบกลับเวอร์ชันล่าสุดในแคช
กลยุทธ์นี้เหมาะกับคำขอ 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 ของโปรแกรมทำงานของบริการ รวมถึง API ที่เกี่ยวข้อง ซึ่งหมายความว่าคุณได้เรียนรู้เพียงพอเกี่ยวกับวิธีใช้ Service Worker โดยตรงเพื่อเริ่มต้นใช้ Workbox แล้ว