เว็บไซต์บางแห่งอาจต้องสื่อสารกับ Service Worker โดยไม่ต้องรับข้อมูลเกี่ยวกับผลลัพธ์ ตัวอย่างเช่น
- หน้าเว็บจะส่งรายการ URL ไปยัง Service Worker เพื่อเตรียมข้อมูลล่วงหน้า เมื่อผู้ใช้คลิกลิงก์ ทรัพยากรย่อยของเอกสารหรือหน้าเว็บจะพร้อมใช้งานในแคชอยู่แล้ว ซึ่งทำให้การไปยังส่วนต่างๆ ในหน้าเว็บในภายหลังเร็วขึ้นมาก
- หน้าเว็บจะขอให้ Service Worker ดึงข้อมูลและแคชชุดบทความยอดนิยมเพื่อให้พร้อมใช้งานแบบออฟไลน์
การมอบหมายงานที่ไม่ใช่งานสําคัญประเภทเหล่านี้ให้กับ Service Worker มีประโยชน์ตรงที่จะช่วยเพิ่มพื้นที่ว่างในเธรดหลักเพื่อจัดการงานที่เร่งด่วนมากขึ้น เช่น การตอบสนองต่อการโต้ตอบของผู้ใช้
ในคู่มือนี้ เราจะสำรวจวิธีใช้เทคนิคการสื่อสารแบบทางเดียวจากหน้าเว็บให้กับ Service Worker โดยใช้ API เบราว์เซอร์มาตรฐานและไลบรารีเวิร์กบ็อกซ์ เราจะเรียกกรณีการใช้งานประเภทนี้ว่าการแคชที่จำเป็น
เคสการผลิต
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',
});
โดย Service Worker จะใช้เครื่องจัดการ 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") โปรแกรมทำงานของบริการอาจแตกแขนงไปยังเส้นทางการดำเนินการต่างๆ โดยอิงตามแฟล็กนี้
หากการดำเนินการสำเร็จ ผู้ใช้จะได้รับสิทธิประโยชน์จากการดำเนินการดังกล่าว แต่หากไม่สำเร็จ การดำเนินการดังกล่าวจะไม่เปลี่ยนแปลงขั้นตอนหลักของผู้ใช้ ตัวอย่างเช่น เมื่อ 1-800-Flowers.com พยายามแคชล่วงหน้า หน้าเว็บไม่จำเป็นต้องทราบว่า Service Worker สำเร็จหรือไม่ หากใช่ ผู้ใช้จะไปยังส่วนต่างๆ ได้เร็วขึ้น หากไม่ได้ผล หน้าเว็บจะต้องไปยังหน้าใหม่ โปรดรออีกสักครู่
ตัวอย่างการเรียกข้อมูลล่วงหน้าแบบง่าย
การใช้งานการแคชแบบบังคับที่พบบ่อยที่สุดอย่างหนึ่งคือการเรียกข้อมูลล่วงหน้า ซึ่งหมายถึงการดึงข้อมูลสำหรับ URL หนึ่งๆ ก่อนที่จะมีผู้ใช้ไปยัง URL นั้น เพื่อเพิ่มความเร็วในการไปยังส่วนต่างๆ
การใช้การจําล่วงหน้าในเว็บไซต์มีหลายวิธีดังนี้
- การใช้แท็กการดึงข้อมูลลิงก์ล่วงหน้าในหน้าเว็บ: ระบบจะเก็บทรัพยากรไว้ในแคชของเบราว์เซอร์เป็นเวลา 5 นาที หลังจากนั้นกฎ
Cache-Control
ปกติสำหรับทรัพยากรจะมีผล - เสริมเทคนิคก่อนหน้าด้วยกลยุทธ์การแคชรันไทม์ในผู้ปฏิบัติงานบริการเพื่อยืดอายุการใช้งานของทรัพยากรการดึงข้อมูลล่วงหน้าเกินขีดจำกัดนี้
สำหรับสถานการณ์การเรียกข้อมูลล่วงหน้าที่ค่อนข้างง่าย เช่น การเรียกข้อมูลล่วงหน้าของเอกสารหรือชิ้นงานเฉพาะ (JS, CSS ฯลฯ) เทคนิคเหล่านี้เป็นแนวทางที่ดีที่สุด
หากจำเป็นต้องใช้ตรรกะเพิ่มเติม เช่น การแยกวิเคราะห์ทรัพยากรที่ดึงข้อมูลล่วงหน้า (ไฟล์ JSON หรือหน้า) เพื่อดึงข้อมูล URL ภายใน การมอบหมายงานนี้ให้กับโปรแกรมทำงานของบริการเพียงอย่างเดียวจะเหมาะสมกว่า
การมอบหมายการดำเนินการประเภทเหล่านี้ให้กับ 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',
],
},
});
ในโปรแกรมทำงานของบริการ ให้ใช้เครื่องจัดการ 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 (เช่น การดาวน์โหลดขนาดใหญ่) และแจ้งข้อมูลความคืบหน้าให้หน้าเว็บทราบอยู่เสมอ