จนถึงตอนนี้ มีเพียงการพูดถึงและข้อมูลโค้ดสั้นๆ ของ อินเทอร์เฟซ 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เพื่อค้นหาการตอบกลับที่แคชไว้ในอินสแตนซ์CacheCache.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 แล้ว