เว็บไซต์บางแห่งอาจต้องสื่อสารกับ Service Worker โดยไม่ต้องรับข้อมูลเกี่ยวกับผลลัพธ์ ตัวอย่างเช่น
- หน้าเว็บจะส่งรายการ URL ไปยัง Service Worker เพื่อเตรียมข้อมูลล่วงหน้า เมื่อผู้ใช้คลิกลิงก์ ทรัพยากรย่อยของเอกสารหรือหน้าเว็บจะพร้อมใช้งานในแคชอยู่แล้ว ซึ่งทำให้การไปยังส่วนต่างๆ ในหน้าเว็บในภายหลังเร็วขึ้นมาก
- หน้าเว็บจะขอให้ Service Worker ดึงข้อมูลและแคชชุดบทความยอดนิยมเพื่อให้พร้อมใช้งานแบบออฟไลน์
การมอบหมายงานที่ไม่ใช่งานสำคัญประเภทเหล่านี้ให้กับ Service Worker มีประโยชน์ตรงที่จะช่วยเพิ่มพื้นที่ว่างในเธรดหลักเพื่อจัดการงานที่เร่งด่วนกว่า เช่น การตอบสนองต่อการโต้ตอบของผู้ใช้
ในคู่มือนี้ เราจะดูวิธีใช้เทคนิคการสื่อสารแบบทางเดียวจากหน้าเว็บไปยัง Service Worker โดยใช้ API เบราว์เซอร์มาตรฐานและไลบรารี Workbox เราจะเรียกกรณีการใช้งานประเภทนี้ว่าการแคชแบบบังคับ
เคสเวอร์ชันที่ใช้งานจริง
1-800-Flowers.com ใช้แคชแบบบังคับ (การเรียกข้อมูลล่วงหน้า) กับ Service Worker ผ่าน postMessage()
เพื่อเรียกข้อมูลล่วงหน้าสำหรับสินค้ายอดนิยมในหน้าหมวดหมู่เพื่อเร่งความเร็วในการไปยังหน้ารายละเอียดผลิตภัณฑ์ในภายหลัง
โดยจะใช้แนวทางแบบผสมในการเลือกรายการที่จะโหลดล่วงหน้า ดังนี้
- เมื่อโหลดหน้าเว็บ ระบบจะขอให้ผู้ปฏิบัติงานของผู้ให้บริการดึงข้อมูล JSON สำหรับรายการยอดนิยม 9 รายการ และเพิ่มออบเจ็กต์การตอบกลับที่ได้ลงในแคช
- สำหรับรายการที่เหลือ ระบบจะคอยฟังเหตุการณ์
mouseover
เพื่อให้สามารถเรียกใช้การดึงข้อมูลทรัพยากรแบบ "ตามต้องการ" เมื่อผู้ใช้เลื่อนเคอร์เซอร์เหนือรายการ
โดยจะใช้ Cache API เพื่อจัดเก็บคําตอบ JSON ดังนี้
เมื่อผู้ใช้คลิกรายการ ระบบจะดึงข้อมูล JSON ที่เชื่อมโยงกับรายการนั้นจากแคชได้โดยไม่ต้องไปที่เครือข่าย ซึ่งทำให้ไปยังส่วนต่างๆ ได้เร็วขึ้น
การใช้ Workbox
Workbox มีวิธีง่ายๆ ในการส่งข้อความไปยัง Service Worker ผ่านแพ็กเกจ workbox-window
ซึ่งเป็นชุดโมดูลที่มีไว้เพื่อทำงานในบริบทของหน้าต่าง แพ็กเกจเหล่านี้ช่วยเสริมแพ็กเกจ Workbox อื่นๆ ที่ทำงานใน Service Worker
หากต้องการสื่อสารหน้าเว็บกับ Service Worker ก่อนอื่นให้รับการอ้างอิงออบเจ็กต์ Workbox ไปยัง Service Worker ที่ลงทะเบียนไว้ ดังนี้
const wb = new Workbox('/sw.js');
wb.register();
จากนั้นคุณจะส่งข้อความได้โดยตรงโดยไม่ต้องลงทะเบียน ตรวจสอบการเปิดใช้งาน หรือคิดถึง API การสื่อสารที่เกี่ยวข้อง
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
ผู้ให้บริการจะใช้ตัวแฮนเดิล message
เพื่อรับฟังข้อความเหล่านี้ ตัวเลือกนี้จะแสดงผลลัพธ์หรือไม่ก็ได้ แต่ในกรณีต่อไปนี้ก็ไม่จําเป็น
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PREFETCH') {
// do something
}
});
การใช้ API ของเบราว์เซอร์
หากไลบรารี Workbox ไม่เพียงพอต่อความต้องการ คุณสามารถใช้วิธีต่อไปนี้เพื่อติดตั้งใช้งานการสื่อสารระหว่างกรอบเวลากับบริการโดยใช้เบราว์เซอร์ API
postMessage API ใช้เพื่อสร้างกลไกการสื่อสารแบบทางเดียวจากหน้าเว็บไปยัง Service Worker ได้
หน้าเว็บเรียกใช้ postMessage()
ในอินเทอร์เฟซ Service Worker ดังนี้
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
payload: 'some data to perform the task',
});
ผู้ให้บริการจะใช้ตัวแฮนเดิล message
เพื่อรับฟังข้อความเหล่านี้
self.addEventListener('message', (event) => {
if (event.data && event.data.type === MSG_ID) {
// do something
}
});
คุณไม่จำเป็นต้องใช้แอตทริบิวต์ {type : 'MSG_ID'}
แต่นี่เป็นวิธีหนึ่งที่ช่วยให้หน้าเว็บส่งคำสั่งประเภทต่างๆ ไปยัง Service Worker ได้ (เช่น "to prefetch" เทียบกับ "to clear storage") เซอร์วิสเวิร์กสามารถแยกออกเป็นเส้นทางการดําเนินการที่แตกต่างกันตาม Flag นี้
หากการดำเนินการสำเร็จ ผู้ใช้จะได้รับสิทธิประโยชน์จากการดำเนินการดังกล่าว แต่หากไม่สำเร็จ การดำเนินการดังกล่าวจะไม่เปลี่ยนแปลงขั้นตอนหลักของผู้ใช้ ตัวอย่างเช่น เมื่อ 1-800-Flowers.com พยายามแคชล่วงหน้า หน้าเว็บไม่จำเป็นต้องทราบว่า Service Worker ทำงานสำเร็จหรือไม่ หากเป็นเช่นนั้น ผู้ใช้จะไปยังส่วนต่างๆ ได้เร็วขึ้น หากไม่ได้ผล หน้าเว็บจะยังคงต้องไปยังหน้าใหม่ โปรดรออีกสักครู่
ตัวอย่างการเรียกข้อมูลล่วงหน้าแบบง่าย
การใช้งานการแคชแบบบังคับที่พบบ่อยที่สุดอย่างหนึ่งคือการเรียกข้อมูลล่วงหน้า ซึ่งหมายถึงการดึงข้อมูลสำหรับ URL หนึ่งๆ ก่อนที่จะมีผู้ใช้ไปยัง URL นั้น เพื่อเพิ่มความเร็วในการไปยังส่วนต่างๆ
การใช้การโหลดล่วงหน้าในเว็บไซต์มีหลายวิธีดังนี้
- การใช้แท็กการดึงข้อมูลลิงก์ล่วงหน้าในหน้าเว็บ: ระบบจะเก็บทรัพยากรไว้ในแคชของเบราว์เซอร์เป็นเวลา 5 นาที หลังจากนั้นกฎ
Cache-Control
ปกติสำหรับทรัพยากรจะมีผล - เสริมเทคนิคก่อนหน้าด้วยกลยุทธ์การแคชรันไทม์ใน Service Worker เพื่อยืดอายุการใช้งานของทรัพยากรการเรียกข้อมูลล่วงหน้าให้นานกว่าขีดจํากัดนี้
สำหรับสถานการณ์การเรียกข้อมูลล่วงหน้าที่ค่อนข้างง่าย เช่น การเรียกข้อมูลล่วงหน้าของเอกสารหรือชิ้นงานเฉพาะ (JS, CSS ฯลฯ) เทคนิคเหล่านี้เป็นแนวทางที่ดีที่สุด
หากต้องใช้ตรรกะเพิ่มเติม เช่น การแยกวิเคราะห์ทรัพยากรการเรียกข้อมูลล่วงหน้า (ไฟล์หรือหน้า JSON) เพื่อดึงข้อมูล URL ภายใน การส่งมอบงานนี้ทั้งหมดให้กับ Service Worker จะเหมาะสมกว่า
การมอบหมายการดำเนินการประเภทเหล่านี้ให้กับ Service Worker มีข้อดีดังนี้
- ย้ายงานหนักของการดึงข้อมูลและการประมวลผลหลังการดึงข้อมูล (ซึ่งจะเปิดตัวในภายหลัง) ไปยังเธรดรอง ซึ่งจะช่วยให้เธรดหลักมีเวลาจัดการงานที่สําคัญกว่า เช่น การตอบสนองต่อการโต้ตอบของผู้ใช้
- อนุญาตให้ไคลเอ็นต์หลายราย (เช่น แท็บ) ใช้ฟังก์ชันการทำงานทั่วไปซ้ำ และแม้แต่เรียกใช้บริการพร้อมกันโดยไม่บล็อกเธรดหลัก
โหลดหน้ารายละเอียดผลิตภัณฑ์ล่วงหน้า
ก่อนอื่น ให้ใช้ postMessage()
ในอินเทอร์เฟซ Service Worker และส่งอาร์เรย์ของ URL ที่จะแคช
navigator.serviceWorker.controller.postMessage({
type: 'PREFETCH',
payload: {
urls: [
'www.exmaple.com/apis/data_1.json',
'www.exmaple.com/apis/data_2.json',
],
},
});
ใน Service Worker ให้ใช้ตัวแฮนเดิล message
เพื่อดักรับและประมวลผลข้อความที่ส่งโดยแท็บที่ใช้งานอยู่
addEventListener('message', (event) => {
let data = event.data;
if (data && data.type === 'PREFETCH') {
let urls = data.payload.urls;
for (let i in urls) {
fetchAsync(urls[i]);
}
}
});
ในโค้ดก่อนหน้า เราได้แนะนำฟังก์ชันตัวช่วยเล็กๆ ชื่อ fetchAsync()
เพื่อวนซ้ำในอาร์เรย์ของ URL และส่งคำขอดึงข้อมูลสำหรับแต่ละรายการ
async function fetchAsync(url) {
// await response of fetch call
let prefetched = await fetch(url);
// (optionally) cache resources in the service worker storage
}
เมื่อได้รับคำตอบแล้ว คุณจะใช้ส่วนหัวการแคชของทรัพยากรได้ อย่างไรก็ตาม ในหลายกรณี เช่น ในหน้ารายละเอียดผลิตภัณฑ์ ระบบจะไม่แคชทรัพยากร (ซึ่งหมายความว่ามีส่วนหัว Cache-control
ของ no-cache
) ในกรณีเช่นนี้ คุณสามารถลบล้างลักษณะการทำงานนี้ได้โดยจัดเก็บทรัพยากรที่ดึงข้อมูลไว้ในแคช Service Worker ซึ่งมีข้อดีเพิ่มเติมคืออนุญาตให้แสดงไฟล์ในสถานการณ์ออฟไลน์
นอกเหนือจากข้อมูล JSON
เมื่อดึงข้อมูล JSON จากปลายทางเซิร์ฟเวอร์แล้ว ข้อมูลดังกล่าวมักจะมี URL อื่นๆ ที่ควรมีการเรียกข้อมูลล่วงหน้าด้วย เช่น รูปภาพหรือข้อมูลปลายทางอื่นๆ ที่เชื่อมโยงกับข้อมูลระดับแรกนี้
สมมติว่าในตัวอย่างนี้ ข้อมูล JSON ที่แสดงผลคือข้อมูลของเว็บไซต์ช็อปปิ้งของใช้ทั่วไป
{
"productName": "banana",
"productPic": "https://cdn.example.com/product_images/banana.jpeg",
"unitPrice": "1.99"
}
แก้ไขโค้ด fetchAsync()
ให้วนซ้ำรายการผลิตภัณฑ์และแคชรูปภาพหลักสำหรับผลิตภัณฑ์แต่ละรายการ
async function fetchAsync(url, postProcess) {
// await response of fetch call
let prefetched = await fetch(url);
//(optionally) cache resource in the service worker cache
// carry out the post fetch process if supplied
if (postProcess) {
await postProcess(prefetched);
}
}
async function postProcess(prefetched) {
let productJson = await prefetched.json();
if (productJson && productJson.product_pic) {
fetchAsync(productJson.product_pic);
}
}
คุณสามารถเพิ่มการจัดการข้อยกเว้นบางส่วนในโค้ดนี้สำหรับสถานการณ์ต่างๆ เช่น 404 แต่ข้อดีของการใช้ Service Worker เพื่อทำการโหลดล่วงหน้าคือการดำเนินการอาจล้มเหลวได้โดยไม่ส่งผลเสียมากนักต่อหน้าเว็บและเธรดหลัก นอกจากนี้ คุณอาจมีตรรกะที่ละเอียดยิ่งขึ้นในการประมวลผลผลลัพธ์ของเนื้อหาที่ดึงข้อมูลไว้ล่วงหน้า ซึ่งทำให้มีความยืดหยุ่นมากขึ้นและแยกจากข้อมูลที่จัดการ ไร้ขีดจำกัด
บทสรุป
ในบทความนี้ เราจะพูดถึงกรณีการใช้งานทั่วไปของการสื่อสารแบบทางเดียวระหว่างหน้าเว็บกับ Service Worker ซึ่งก็คือการแคชแบบบังคับ ตัวอย่างที่กล่าวถึงมีไว้เพื่อสาธิตวิธีใช้รูปแบบนี้เพียงวิธีเดียวเท่านั้น และสามารถใช้แนวทางเดียวกันกับ Use Case อื่นๆ ได้ด้วย เช่น การแคชบทความยอดนิยมตามคําขอสําหรับการใช้งานแบบออฟไลน์ การบุ๊กมาร์ก และอื่นๆ
ดูรูปแบบการสื่อสารเพิ่มเติมระหว่างหน้าเว็บและ Service Worker ได้ที่
- การออกอากาศการอัปเดต: การเรียกหน้าเว็บจาก Service Worker เพื่อแจ้งเกี่ยวกับการอัปเดตที่สําคัญ (เช่น เว็บแอปเวอร์ชันใหม่พร้อมใช้งานแล้ว)
- การสื่อสารแบบ 2 ทาง: การมอบหมายงานให้กับ Service Worker (เช่น การดาวน์โหลดขนาดใหญ่) และแจ้งข้อมูลความคืบหน้าให้หน้าเว็บทราบอยู่เสมอ