บางเว็บไซต์อาจต้องสื่อสารกับโปรแกรมทำงานของบริการโดยไม่จำเป็นต้องได้รับข้อมูลผลลัพธ์ โดยมีตัวอย่างดังนี้
- หน้าเว็บจะส่งรายการ URL เพื่อดึงข้อมูลล่วงหน้าให้กับโปรแกรมทำงาน เพื่อที่เมื่อผู้ใช้คลิกลิงก์ ทรัพยากรย่อยของเอกสารหรือหน้าเว็บอยู่ในแคชอยู่แล้ว ซึ่งทำให้การนำทางที่ตามมานั้นรวดเร็วขึ้นมาก
- หน้าเว็บจะขอให้โปรแกรมทำงานของบริการเรียกข้อมูลและแคชชุดบทความยอดนิยม เพื่อให้บทความเหล่านั้นพร้อมใช้งานแบบออฟไลน์
การมอบสิทธิ์งานที่ไม่สำคัญประเภทนี้ให้กับโปรแกรมทำงานของบริการมีประโยชน์ในการเพิ่มพื้นที่ว่างสำหรับเทรดหลักเพื่อให้สามารถจัดการงานเร่งด่วนต่างๆ ได้ดียิ่งขึ้น เช่น การตอบสนองการโต้ตอบของผู้ใช้
![แผนภาพของหน้าที่มีคำขอทรัพยากรเพื่อแคชไปยังโปรแกรมทำงานของบริการ](https://web.dev/static/articles/imperative-caching-guide/image/diagram-a-page-requestin-f19107d4e4aec.png?authuser=6&hl=th)
ในคู่มือนี้ เราจะสำรวจวิธีใช้เทคนิคการสื่อสารแบบทางเดียวจากหน้าเว็บไปยัง Service Worker โดยใช้ API เบราว์เซอร์มาตรฐานและไลบรารี Workbox เราจะเรียกกรณีการใช้งานประเภทนี้ว่า การแคชที่จำเป็น
กรณีการผลิต
1-800-Flowers.com ใช้การแคชที่จำเป็น (การดึงข้อมูลล่วงหน้า) กับโปรแกรมทำงานของบริการผ่าน postMessage()
เพื่อดึงข้อมูลสินค้ายอดนิยมในหน้าหมวดหมู่ล่วงหน้า เพื่อเร่งความเร็วในการไปยังส่วนต่างๆ ของหน้ารายละเอียดผลิตภัณฑ์ในภายหลัง
![โลโก้ 1-800 Flowers](https://web.dev/static/articles/imperative-caching-guide/image/logo-1-800-flowers-94f008b879df2.png?authuser=6&hl=th)
โดยใช้แนวทางแบบผสมผสานในการตัดสินใจว่าจะดึงข้อมูลใดล่วงหน้า
- ในการโหลดหน้าเว็บ ผู้ปฏิบัติงานจะขอให้ Servicer Worker ดึงข้อมูล JSON สำหรับรายการ 9 อันดับแรก และเพิ่มออบเจ็กต์การตอบสนองที่ได้ลงในแคช
- สำหรับรายการที่เหลือ ผู้ใช้จะคอยฟังเหตุการณ์
mouseover
ดังนั้นเมื่อผู้ใช้เลื่อนเคอร์เซอร์ไปวางเหนือรายการ ก็จะทริกเกอร์การดึงข้อมูลทรัพยากรแบบ "ดีมานด์" ได้
โดยใช้ Cache API เพื่อจัดเก็บการตอบสนองของ JSON ดังนี้
![โลโก้ 1-800 Flowers](https://web.dev/static/articles/imperative-caching-guide/image/logo-1-800-flowers-ee1b000c680c6.png?authuser=6&hl=th)
เมื่อผู้ใช้คลิกที่รายการ ระบบจะดึงข้อมูล JSON ที่เชื่อมโยงกับรายการนั้นจากแคชได้โดยไม่ต้องไปยังเครือข่าย ทำให้การนำทางรวดเร็วขึ้น
การใช้ Workbox
Workbox เป็นวิธีง่ายๆ ในการส่งข้อความไปยัง Service Worker ผ่านแพ็กเกจ workbox-window
ซึ่งเป็นชุดโมดูลที่มีจุดประสงค์ให้ทำงานในบริบทของหน้าต่าง ซึ่งเป็นส่วนเสริมของแพ็กเกจ Workbox
อื่นๆ ที่ทำงานในโปรแกรมทำงานของบริการ
หากต้องการสื่อสารหน้านี้กับ Service Worker ให้ขอการอ้างอิงออบเจ็กต์ของ Workbox ไปยัง Service Worker ที่ลงทะเบียนไว้ก่อน ดังนี้
const wb = new Workbox('/sw.js');
wb.register();
จากนั้นคุณจะส่งข้อความประกาศได้โดยตรง โดยไม่ต้องยุ่งยากกับการลงทะเบียน ตรวจสอบการเปิดใช้งาน หรือไม่ต้องกังวลเรื่อง API การสื่อสารที่สำคัญ
wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });
Service Worker จะใช้ตัวแฮนเดิล 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 ได้ (ซึ่งก็คือ "การดึงข้อมูลล่วงหน้า" กับ "เพื่อล้างพื้นที่เก็บข้อมูล") Service Worker สามารถแตกข้อมูลไปยังเส้นทางการดำเนินการต่างๆ ตามแฟล็กนี้
หากดำเนินการสำเร็จ ผู้ใช้จะได้รับประโยชน์จากการดำเนินการดังกล่าว แต่หากไม่เป็นเช่นนั้น ก็จะไม่เปลี่ยนแปลงขั้นตอนหลักของผู้ใช้ ตัวอย่างเช่น เมื่อ 1-800-Flowers.com พยายามทำการแคชล่วงหน้า หน้าเว็บไม่จำเป็นต้องทราบว่าโปรแกรมทำงานของบริการประสบความสำเร็จหรือไม่ ซึ่งถ้าเป็นเช่นนั้น ผู้ใช้จะได้เพลิดเพลินไปกับการนำทางที่รวดเร็วยิ่งขึ้น หากไม่ปรากฏ หน้านั้นก็ยังคงต้องไปยังหน้าใหม่ จะใช้เวลานานกว่านี้นะ
ตัวอย่างการดึงข้อมูลล่วงหน้าแบบง่ายๆ
การใช้งานการแคชที่จำเป็นที่พบบ่อยที่สุดอย่างหนึ่งคือการดึงข้อมูลล่วงหน้า ซึ่งหมายถึงการดึงข้อมูลทรัพยากรสำหรับ URL หนึ่งๆ ก่อนที่ผู้ใช้จะย้ายไปยัง URL เพื่อให้ไปยังส่วนต่างๆ ได้เร็วขึ้น
การดึงข้อมูลล่วงหน้าในเว็บไซต์มีหลายวิธีดังนี้
- ใช้แท็กการดึงข้อมูลล่วงหน้าของลิงก์ในหน้า: ทรัพยากรจะเก็บไว้ในแคชของเบราว์เซอร์เป็นเวลา 5 นาที หลังจากนั้นกฎ
Cache-Control
ปกติสำหรับทรัพยากรจะมีผล - เสริมเทคนิคก่อนหน้าด้วยกลยุทธ์การแคชรันไทม์ในโปรแกรมทำงานของบริการเพื่อยืดอายุการใช้งานของทรัพยากรที่ดึงข้อมูลล่วงหน้าให้เกินขีดจำกัดนี้
สำหรับสถานการณ์การดึงข้อมูลล่วงหน้าที่ค่อนข้างง่าย เช่น การดึงข้อมูลเอกสารล่วงหน้า หรือเนื้อหาเฉพาะ (JS, CSS เป็นต้น) เทคนิคเหล่านั้นคือวิธีที่ดีที่สุด
หากต้องใช้ตรรกะเพิ่มเติม เช่น การแยกวิเคราะห์ทรัพยากรการดึงข้อมูลล่วงหน้า (ไฟล์หรือหน้า JSON) เพื่อดึงข้อมูล URL ภายใน ที่เหมาะสมกว่าที่จะมอบหมายงานนี้ให้กับโปรแกรมทำงานทั้งหมด
การมอบสิทธิ์การดำเนินการประเภทเหล่านี้ให้กับโปรแกรมทำงานของบริการมีข้อดีดังต่อไปนี้
- ลดภาระงานหนักของการดึงข้อมูลและการประมวลผลหลังการดึงข้อมูล (ซึ่งจะแนะนําในภายหลัง) ในชุดข้อความรอง การทําเช่นนี้จะช่วยให้เทรดหลักจัดการงานสําคัญๆ ได้มากขึ้น เช่น การตอบสนองการโต้ตอบของผู้ใช้
- อนุญาตให้ไคลเอ็นต์หลายตัว (เช่น แท็บ) ใช้ฟังก์ชันการทำงานทั่วไปซ้ำ และแม้กระทั่งเรียกใช้บริการพร้อมกันโดยไม่บล็อกเทรดหลัก
ดึงข้อมูลหน้ารายละเอียดผลิตภัณฑ์ล่วงหน้า
ให้ใช้ 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 และส่งคำขอดึงข้อมูลสำหรับแต่ละ 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 เพื่อดึงข้อมูลล่วงหน้าคือการทำงานจะล้มเหลวได้โดยที่ไม่ส่งผลต่อหน้าเว็บและเทรดหลักมากนัก นอกจากนี้ คุณยังอาจมีตรรกะที่ซับซ้อนมากขึ้นในขั้นตอนหลังการประมวลผลเนื้อหาที่ดึงข้อมูลล่วงหน้า ซึ่งทำให้มีความยืดหยุ่นมากขึ้นและแยกจากข้อมูลที่ต้องจัดการ ไร้ขีดจำกัด
บทสรุป
ในบทความนี้ เราจะพูดถึง Use Case ทั่วไปของการสื่อสารแบบทางเดียวระหว่างหน้าเว็บและ Service Worker นั่นก็คือ การแคชที่จำเป็น ตัวอย่างที่พูดถึงมีขึ้นเพื่อสาธิตการใช้รูปแบบนี้เพียงวิธีเดียว และสามารถใช้แนวทางเดียวกันนี้กับ Use Case อื่นๆ ได้ด้วย เช่น การแคชบทความยอดนิยมสำหรับการใช้งานแบบออฟไลน์ การบุ๊กมาร์ก และอื่นๆ
สำหรับรูปแบบเพิ่มเติมของการสื่อสารในหน้าเว็บและ Service Worker โปรดดูที่:
- อัปเดตการประกาศ: เรียกใช้หน้าเว็บจาก Service Worker เพื่อแจ้งการอัปเดตที่สำคัญ (เช่น มีเว็บแอปเวอร์ชันใหม่แล้ว)
- การสื่อสารแบบ 2 ทาง: การมอบหมายงานให้กับ Service Worker (เช่น การดาวน์โหลดจำนวนมาก) และการแจ้งความคืบหน้าในหน้าเว็บ