ปัจจุบันมีเพียงการพูดถึงและข้อมูลโค้ดสั้นๆ ของอินเทอร์เฟซ 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!