การที่จะรู้ว่าพนักงานบริการกำลังทำอะไรอยู่หากไม่เข้าใจวงจรชีวิตของพวกเขา การทำงานภายในของเด็กๆ จะดูคลุมเครือแม้จะเป็นไปโดยไม่มีกฎเกณฑ์ โปรดอย่าลืมว่าลักษณะการทำงานของ Service Worker นั้นเหมือนกับ API เบราว์เซอร์อื่นๆ ตรงที่มีการระบุ กำหนด และทำให้แอปพลิเคชันออฟไลน์เป็นไปได้ ขณะเดียวกันก็อำนวยความสะดวกในการอัปเดตโดยไม่รบกวนประสบการณ์ของผู้ใช้
ก่อนเจาะลึกเข้าไปใน Workbox คุณต้องทำความเข้าใจวงจรการทำงานของโปรแกรมทำงานของบริการเพื่อให้ Workbox ใช้ได้อย่างเหมาะสม
นิยามคำศัพท์
ก่อนที่จะเข้าสู่วงจรการทำงานของโปรแกรมทำงาน คุณควรนิยามคำศัพท์เกี่ยวกับวิธีการทำงานของวงจรดังกล่าว
การควบคุมและขอบเขต
แนวคิดเกี่ยวกับการควบคุมมีความสำคัญอย่างยิ่งต่อการเข้าใจการทำงานของโปรแกรมทำงานของบริการ หน้าที่มีการระบุว่ามีการควบคุมโดย Service Worker คือหน้าที่อนุญาตให้โปรแกรมทำงานของบริการสกัดกั้นคำขอของเครือข่ายในนามของตนเองได้ มี Service Worker และสามารถทำงานให้กับหน้าเว็บภายในขอบเขตที่กำหนดได้
ขอบเขต
ขอบเขตของ Service Worker พิจารณาจากตำแหน่งในเว็บเซิร์ฟเวอร์
หาก Service Worker ทำงานในหน้าเว็บที่ /subdir/index.html
และอยู่ที่ /subdir/sw.js
ขอบเขตของโปรแกรมทำงานของบริการจะเป็น /subdir/
หากต้องการดูแนวคิดของการใช้งานจริงของขอบเขต โปรดดูตัวอย่างนี้
- ไปที่ https://service-worker-scope-viewer.glitch.me/subdir/index.html
จะมีข้อความปรากฏขึ้นว่าไม่มี Service Worker ที่ควบคุมหน้านี้อยู่
แต่หน้าเว็บนั้นจะลงทะเบียน Service Worker จาก
https://service-worker-scope-viewer.glitch.me/subdir/sw.js
- โหลดหน้าเว็บซ้ำ เนื่องจาก Service Worker ลงทะเบียนแล้วและกำลังทำงานอยู่ จึงเป็นการควบคุมหน้าเว็บ แบบฟอร์มที่มีขอบเขต สถานะปัจจุบันของโปรแกรมทำงาน และ URL ของ Service Worker จะปรากฏขึ้น หมายเหตุ: การต้องโหลดหน้าเว็บซ้ำไม่ได้เกี่ยวข้องกับขอบเขตนี้เลย แต่เกี่ยวข้องกับวงจรการทำงานของโปรแกรมทำงานของบริการ ซึ่งจะได้อธิบายในภายหลัง
- จากนั้นไปที่ https://service-worker-scope-viewer.glitch.me/index.html แม้ว่าจะมีการลงทะเบียน Service Worker ในต้นทางนี้ แต่ยังคงมีข้อความแจ้งว่าไม่มี Service Worker อยู่ในขณะนี้ เนื่องจากหน้านี้ไม่ได้อยู่ในขอบเขตของ Service Worker ที่ลงทะเบียนไว้
ขอบเขตจะจำกัดหน้าที่โปรแกรมทำงานของบริการควบคุม
ในตัวอย่างนี้หมายความว่า Service Worker ที่โหลดจาก /subdir/sw.js
จะควบคุมได้เฉพาะหน้าที่อยู่ใน /subdir/
หรือแผนผังย่อยเท่านั้น
วิธีการข้างต้นคือวิธีกำหนดขอบเขตโดยค่าเริ่มต้น
แต่ลบล้างขอบเขตสูงสุดที่อนุญาตได้ด้วยการตั้งค่าส่วนหัวการตอบกลับ
Service-Worker-Allowed
รวมถึงส่ง
ตัวเลือก scope
ไปยังเมธอด register
เว้นแต่จะมีเหตุผลที่ดีในการจำกัดขอบเขต Service Worker ไว้เฉพาะบางส่วนของต้นทาง ให้โหลด Service Worker จากไดเรกทอรีรูทของเว็บเซิร์ฟเวอร์เพื่อให้ขอบเขตกว้างที่สุดและไม่ต้องกังวลเรื่องส่วนหัว Service-Worker-Allowed
ซึ่งทำได้ง่ายกว่ามากสำหรับทุกคน
ลูกค้า
เมื่อกล่าวว่าโปรแกรมทำงานของบริการควบคุมหน้าเว็บ การทำงานของบริการจะทำหน้าที่ควบคุมไคลเอ็นต์อย่างแท้จริง
ไคลเอ็นต์คือหน้าเว็บที่เปิดอยู่ซึ่งมี URL อยู่ในขอบเขตของ Service Worker
โดยเฉพาะอย่างยิ่ง สิ่งเหล่านี้คือตัวอย่างของ WindowClient
วงจรการทำงานของ Service Worker ใหม่
ถ้าจะให้โปรแกรมทำงานของบริการควบคุมหน้าเว็บ จะต้องทำให้หน้านั้นมีอยู่แล้วก่อน เรามาเริ่มกันที่จะเกิดอะไรขึ้นเมื่อมีการทำให้ Service Worker ใหม่กับเว็บไซต์ที่ไม่มี Service Worker ที่ใช้งานอยู่
การลงทะเบียน
การลงทะเบียนคือขั้นตอนเริ่มต้นของวงจรการทำงานของโปรแกรมทำงานของบริการ ดังนี้
<!-- In index.html, for example: -->
<script>
// Don't register the service worker
// until the page has fully loaded
window.addEventListener('load', () => {
// Is service worker available?
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('Service worker registered!');
}).catch((error) => {
console.warn('Error registering service worker:');
console.warn(error);
});
}
});
</script>
โค้ดนี้จะทำงานในเทรดหลักและจะทำงานต่อไปนี้
- เนื่องจากการเข้าชมเว็บไซต์ครั้งแรกของผู้ใช้เกิดขึ้นโดยไม่มี Service Worker ที่ลงทะเบียนไว้ โปรดรอจนกว่าหน้าเว็บจะโหลดเสร็จสมบูรณ์ก่อนที่จะลงทะเบียน การทำเช่นนี้จะหลีกเลี่ยงการช่วงชิงแบนด์วิดท์ หากโปรแกรมทำงานของบริการเก็บข้อมูลใดๆ ไว้ล่วงหน้า
- แม้ว่าจะมีการรองรับโปรแกรมทำงานของบริการไว้เป็นอย่างดี แต่การตรวจสอบอย่างรวดเร็วจะช่วยหลีกเลี่ยงข้อผิดพลาดในเบราว์เซอร์ที่ไม่รองรับได้
- เมื่อหน้าเว็บโหลดเสร็จแล้ว และหากรองรับ Service Worker ให้ลงทะเบียน
/sw.js
สิ่งสำคัญที่ควรทำความเข้าใจมีดังนี้
- โปรแกรมทำงานของบริการพร้อมใช้งานผ่าน HTTPS หรือ localhost เท่านั้น
- หากเนื้อหาของ Service Worker มีข้อผิดพลาดทางไวยากรณ์ การลงทะเบียนจะไม่สำเร็จ และ Service Worker จะถูกยกเลิก
- หมายเหตุ: โปรแกรมทำงานของบริการดำเนินงานภายในขอบเขต ขอบเขตคือต้นทางทั้งหมด เนื่องจากโหลดจากไดเรกทอรีรูท
- เมื่อเริ่มการลงทะเบียน สถานะของ Service Worker จะตั้งเป็น
'installing'
เมื่อการลงทะเบียนเสร็จสิ้น การติดตั้งจะเริ่มต้นขึ้น
การติดตั้ง
Service Worker เริ่มการทำงานของเหตุการณ์ install
หลังการลงทะเบียน
ระบบจะเรียกใช้ install
เพียงครั้งเดียวต่อ Service Worker และจะไม่เริ่มทำงานอีกจนกว่าจะอัปเดต
ลงทะเบียนโค้ดเรียกกลับสำหรับเหตุการณ์ install
ในขอบเขตของผู้ปฏิบัติงานได้ด้วย addEventListener
:
// /sw.js
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v1';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v1'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.bc7b80b7.css',
'/css/home.fe5d0b23.css',
'/js/home.d3cc4ba4.js',
'/js/jquery.43ca4933.js'
]);
}));
});
การดำเนินการนี้จะสร้างอินสแตนซ์ Cache
ใหม่และแคชเนื้อหาล่วงหน้า
เราจะมีโอกาสได้พูดคุยกันอีกมากในภายหลังเกี่ยวกับการสร้างแคช ดังนั้นเรามาโฟกัสที่บทบาทของ event.waitUntil
กันต่อดีกว่า event.waitUntil
ยอมรับสัญญาและรอจนกว่าคำขอนั้นจะได้รับการแก้ไข
ในตัวอย่างนี้ คำสัญญานั้นจะทำ 2 สิ่งที่ไม่พร้อมกัน ได้แก่
- สร้างอินสแตนซ์
Cache
ใหม่ชื่อ'MyFancyCache_v1'
- หลังจากสร้างแคชแล้ว ระบบจะแคชอาร์เรย์ของ URL เนื้อหาล่วงหน้าโดยใช้เมธอด
addAll
แบบอะซิงโครนัส
การติดตั้งจะไม่สำเร็จหากปฏิเสธคำมั่นสัญญาที่ส่งไปยัง event.waitUntil
ในกรณีนี้ โปรแกรมทำงานของบริการจะถูกยกเลิก
หากคำสัญญาresolve การติดตั้งสำเร็จและสถานะของโปรแกรมทำงานของบริการจะเปลี่ยนเป็น 'installed'
และหลังจากนั้นจะเปิดใช้งาน
การดำเนินการ
หากการลงทะเบียนและติดตั้งสำเร็จ โปรแกรมทำงานของบริการจะเปิดใช้งาน และมีสถานะ 'activating'
คุณจะทำงานระหว่างการเปิดใช้งานในเหตุการณ์ activate
ของโปรแกรมทำงานของบริการได้
งานทั่วไปในเหตุการณ์นี้คือการตัดแคชเก่า
แต่สำหรับ Service Worker ที่เพิ่งเปลี่ยนมานั้นยังไม่เกี่ยวข้องกันในตอนนี้
และเราจะอธิบายเพิ่มเติมถึงเรื่องการอัปเดตโปรแกรมทำงานของบริการ
สำหรับ Service Worker ใหม่ activate
จะเริ่มทำงานทันทีหลังจากที่ install
สำเร็จ
เมื่อการเปิดใช้งานเสร็จสิ้น สถานะของโปรแกรมทำงานของบริการจะกลายเป็น 'activated'
โปรดทราบว่า โดยค่าเริ่มต้น Service Worker ใหม่จะไม่เริ่มควบคุมหน้าจนกว่าจะมีการไปยังส่วนต่างๆ หรือการรีเฟรชหน้าในครั้งถัดไป
การจัดการการอัปเดต Service Worker
เมื่อทำให้ Service Worker แรกใช้งานได้แล้ว อาจต้องมีการอัปเดตในภายหลัง เช่น อาจต้องอัปเดตหากมีการเปลี่ยนแปลงในการจัดการคำขอหรือตรรกะการแคชล่วงหน้า
เมื่อมีการอัปเดต
เบราว์เซอร์จะตรวจหาอัปเดตของโปรแกรมทำงานของบริการในกรณีต่อไปนี้
- ผู้ใช้จะไปยังหน้าเว็บที่อยู่ในขอบเขตของโปรแกรมทำงานของบริการ
navigator.serviceWorker.register()
จะมีการเรียกด้วย URL ที่ต่างจาก Service Worker ที่ติดตั้งอยู่ในปัจจุบัน แต่อย่าเปลี่ยน URL ของ Service Workernavigator.serviceWorker.register()
จะถูกเรียกด้วย URL เดียวกันกับ Service Worker ที่ติดตั้งไว้ แต่มีขอบเขตต่างกัน ขอย้ำว่าให้หลีกเลี่ยงกรณีเช่นนี้โดยคงขอบเขตไว้ที่รูทของต้นทาง หากเป็นไปได้- เมื่อมีการทริกเกอร์เหตุการณ์ เช่น
'push'
หรือ'sync'
ภายใน 24 ชั่วโมงที่ผ่านมา แต่ยังไม่ต้องกังวลเกี่ยวกับเหตุการณ์เหล่านี้
การอัปเดตเกิดขึ้นได้อย่างไร
การทราบเวลาที่เบราว์เซอร์อัปเดตโปรแกรมทำงานของบริการเป็นสิ่งสำคัญ แต่ก็เป็นเรื่อง "วิธีการ" ด้วย สมมติว่า URL หรือขอบเขตของโปรแกรมทำงานของบริการไม่มีการเปลี่ยนแปลง โปรแกรมทำงานของบริการที่ติดตั้งอยู่ในปัจจุบันจะอัปเดตเป็นเวอร์ชันใหม่เท่านั้นหากเนื้อหามีการเปลี่ยนแปลง
เบราว์เซอร์จะตรวจหาการเปลี่ยนแปลงด้วยวิธีต่อไปนี้
- การเปลี่ยนแปลงแบบไบต์ต่อไบต์ในสคริปต์ที่
importScripts
ร้องขอ (หากมี) - การเปลี่ยนแปลงโค้ดระดับบนสุดของ Service Worker ซึ่งจะส่งผลต่อลายนิ้วมือที่เบราว์เซอร์สร้างขึ้น
เบราว์เซอร์ทำงานหลายอย่างในส่วนนี้ หากต้องการตรวจสอบว่าเบราว์เซอร์มีทุกสิ่งที่ต้องใช้ในการตรวจหาการเปลี่ยนแปลงเนื้อหาของ Service Worker ได้อย่างน่าเชื่อถือ โปรดอย่าบอกแคช HTTP ให้เก็บข้อมูลไว้และไม่เปลี่ยนชื่อไฟล์ เบราว์เซอร์จะตรวจสอบการอัปเดตโดยอัตโนมัติเมื่อมีการนำทางไปยังหน้าใหม่ภายในขอบเขตของโปรแกรมทำงานของบริการ
การทริกเกอร์การตรวจสอบการอัปเดตด้วยตนเอง
การอัปเดตที่เกี่ยวข้อง ตรรกะการลงทะเบียนโดยทั่วไปไม่ควรเปลี่ยนแปลง แต่ก็อาจมีข้อยกเว้นอย่างหนึ่งคือหากเซสชันในเว็บไซต์มีระยะเวลายาวนาน กรณีนี้อาจเกิดขึ้นในแอปพลิเคชันหน้าเว็บเดียวที่แทบไม่มีคำขอการนำทาง เนื่องจากโดยทั่วไปแอปพลิเคชันมักจะพบคำขอการนำทาง 1 คำขอที่จุดเริ่มต้นของวงจรของแอปพลิเคชัน ในสถานการณ์เช่นนี้ การอัปเดตด้วยตนเองจะทริกเกอร์ในเทรดหลักได้ ดังนี้
navigator.serviceWorker.ready.then((registration) => {
registration.update();
});
สำหรับเว็บไซต์แบบดั้งเดิม หรือในกรณีที่เซสชันผู้ใช้มีอายุไม่นาน การเรียกใช้การอัปเดตด้วยตนเองอาจไม่จำเป็น
การติดตั้ง
เมื่อใช้ Bundler เพื่อสร้างเนื้อหาแบบคงที่ เนื้อหาเหล่านั้นจะมีแฮชในชื่อ เช่น framework.3defa9d2.js
สมมติว่าเนื้อหาบางส่วนถูกแคชล่วงหน้าไว้สำหรับการเข้าถึงแบบออฟไลน์ในภายหลัง
การดำเนินการนี้จะต้องมีการอัปเดต Service Worker เพื่อแคชเนื้อหาที่อัปเดตล่วงหน้า:
self.addEventListener('install', (event) => {
const cacheKey = 'MyFancyCacheName_v2';
event.waitUntil(caches.open(cacheKey).then((cache) => {
// Add all the assets in the array to the 'MyFancyCacheName_v2'
// `Cache` instance for later use.
return cache.addAll([
'/css/global.ced4aef2.css',
'/css/home.cbe409ad.css',
'/js/home.109defa4.js',
'/js/jquery.38caf32d.js'
]);
}));
});
มี 2 สิ่งที่แตกต่างจากตัวอย่างเหตุการณ์ install
แรกจากก่อนหน้านี้
- ระบบสร้างอินสแตนซ์
Cache
ใหม่ที่มีคีย์'MyFancyCacheName_v2'
แล้ว - ชื่อของเนื้อหาที่จัดเก็บในแคชล่วงหน้ามีการเปลี่ยนแปลง
สิ่งหนึ่งที่ควรทราบคือ Service Worker ที่อัปเดตจะติดตั้งควบคู่กับเวอร์ชันก่อนหน้า ซึ่งหมายความว่าโปรแกรมทำงานของบริการเก่าจะยังคงควบคุมหน้าที่เปิดอยู่ทั้งหมด และหลังจากการติดตั้ง โปรแกรมทำงานใหม่จะเข้าสู่สถานะรอจนกว่าจะมีการเปิดใช้งาน
โดยค่าเริ่มต้น โปรแกรมทำงานของบริการใหม่จะเปิดใช้งานเมื่อไม่มีไคลเอ็นต์เก่าควบคุมอยู่ ซึ่งจะเกิดขึ้นเมื่อปิดแท็บที่เปิดอยู่ทั้งหมดของเว็บไซต์ที่เกี่ยวข้อง
การดำเนินการ
เมื่อมีการติดตั้ง Service Worker ที่อัปเดตและระยะการรอสิ้นสุดลง โปรแกรมจะทำงานและทิ้งโปรแกรมทำงานของบริการเก่าทิ้ง
งานทั่วไปที่ต้องทำในเหตุการณ์ activate
ของ Service Worker ที่อัปเดตคือการตัดแคชเก่า
นำแคชเก่าออกโดยรับคีย์สำหรับอินสแตนซ์ Cache
ที่เปิดอยู่ทั้งหมดด้วย caches.keys
และลบแคชที่ไม่ได้อยู่ในรายการที่อนุญาตที่กำหนดไว้ด้วย caches.delete
:
self.addEventListener('activate', (event) => {
// Specify allowed cache keys
const cacheAllowList = ['MyFancyCacheName_v2'];
// Get all the currently active `Cache` instances.
event.waitUntil(caches.keys().then((keys) => {
// Delete all caches that aren't in the allow list:
return Promise.all(keys.map((key) => {
if (!cacheAllowList.includes(key)) {
return caches.delete(key);
}
}));
}));
});
แคชเก่าจะไม่จัดการให้เป็นระเบียบ
เราจำเป็นต้องดำเนินการดังกล่าวเองหรือเสี่ยงต่อการใช้เกินโควต้าพื้นที่เก็บข้อมูล
เนื่องจาก 'MyFancyCacheName_v1'
จาก Service Worker แรกล้าสมัยแล้ว รายการแคชที่อนุญาตจึงได้รับการอัปเดตเพื่อระบุ 'MyFancyCacheName_v2'
ซึ่งจะลบแคชที่มีชื่ออื่น
เหตุการณ์ activate
จะสิ้นสุดหลังจากนำแคชเก่าออก
เมื่อถึงจุดนี้ Service Worker ใหม่จะควบคุมหน้าเว็บ
โดยมาแทนที่หน้าเก่าในท้ายที่สุด
ใช้งานได้อย่างไม่มีที่สิ้นสุด
ไม่ว่าจะมีการใช้ Workbox เพื่อจัดการการทำให้ใช้งานได้และการอัปเดตโปรแกรมทำงานของบริการ หรือมีการใช้ Service Worker API โดยตรงหรือไม่ การจ่ายงานเพื่อให้เข้าใจวงจรการทำงานของโปรแกรมทำงานของบริการ ด้วยความเข้าใจนี้ พฤติกรรมการทำงานของพนักงานบริการควรฟังดูสมเหตุสมผลมากกว่าที่จะลึกลับ
สำหรับผู้ที่สนใจเจาะลึกเรื่องนี้มากขึ้น ก็ให้อ่านบทความนี้โดย Jake Archibald การเต้นในวงจรชีวิตของบริการมีความแตกต่างกันได้มากมาย แต่ก็ทราบได้ และความรู้ที่ได้จะพัฒนาไปไกลมากเมื่อใช้ Workbox