ในบางกรณี เว็บแอปอาจต้องสร้างช่องทางการสื่อสารแบบ2 ทางระหว่าง และ Service Worker
ตัวอย่างเช่น ใน PWA ของพอดแคสต์ ผู้ใช้อาจสร้างฟีเจอร์เพื่อให้ผู้ใช้ดาวน์โหลดตอนต่างๆ ได้ การใช้งานออฟไลน์ และอนุญาตให้ ของโปรแกรมทำงานของบริการ เพื่อคอยแจ้งความคืบหน้าในหน้าเว็บเป็นประจำ ดังนั้น เทรดอัปเดต UI ได้
ในคู่มือนี้เราจะสำรวจวิธีต่างๆ ในการใช้การสื่อสารแบบ 2 ทางระหว่าง หน้าต่างและบริการ ผู้ปฏิบัติงานจากการสำรวจ API ต่างๆ ไลบรารีพื้นที่ทำงาน รวมทั้ง กรณีพิเศษบางอย่าง
การใช้ Workbox
workbox-window
เป็นชุดของ
โมดูลของไลบรารีพื้นที่ทำงานที่ต้องการ
เพื่อเรียกใช้ในบริบทของหน้าต่าง Workbox
คลาส มีเมธอด messageSW()
เพื่อส่งข้อความไปยัง Service Worker ที่ลงทะเบียนของอินสแตนซ์และ
รอการตอบกลับ
โค้ดของหน้าเว็บต่อไปนี้จะสร้างอินสแตนซ์ Workbox
ใหม่และส่งข้อความไปยัง Service Worker
เพื่อดูเวอร์ชัน:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
โปรแกรมทำงานของบริการจะใช้ Listener ข้อความในอีกด้านหนึ่ง และตอบกลับ Service Worker:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
เบื้องหลังการทำงาน ไลบรารีจะใช้ API เบราว์เซอร์ที่เราจะตรวจสอบในส่วนถัดไป: ข้อความ ช่อง แต่แอบสแตรกต์จำนวนมาก รายละเอียดการนำไปใช้งาน ทำให้ใช้งานง่าย ขณะเดียวกันก็ใช้ประโยชน์จากเบราว์เซอร์แบบกว้าง รองรับ API นี้
การใช้ API ของเบราว์เซอร์
หากไลบรารีของ Workbox ไม่เพียงพอสำหรับความต้องการของคุณ เรามี API ระดับล่างหลายรายการที่พร้อมให้คุณใช้งาน ใช้การสื่อสารแบบ "2 ทาง" ระหว่างเพจและโปรแกรมทำงานของบริการ ผลการค้นหาคล้ายกัน และความแตกต่าง ได้แก่
ความคล้ายคลึงกัน:
- ในทุกกรณี การสื่อสารจะเริ่มต้นทางฝั่งหนึ่งทางอินเทอร์เฟซ
postMessage()
และ ในอีกฝั่งหนึ่งโดยใช้ตัวจัดการmessage
- ในทางปฏิบัติแล้ว API ทั้งหมดที่มีอยู่จะช่วยให้เราใช้งานกรณีการใช้งานเดียวกันได้ แต่ API บางรายการ อาจช่วยให้การพัฒนาง่ายขึ้นในบางสถานการณ์
ความแตกต่าง:
- แต่ละคนมีวิธีที่แตกต่างกันในการระบุอีกด้านของการสื่อสาร โดยบางคนใช้ มีการอ้างอิงถึงบริบทอื่นอย่างชัดแจ้ง ขณะที่ผู้อื่นสามารถสื่อสารโดยนัยผ่านพร็อกซีได้ ที่สร้างอินสแตนซ์แต่ละด้าน
- การรองรับเบราว์เซอร์จะแตกต่างกันไป
API ช่องสำหรับการออกอากาศ
Broadcast Channel API อนุญาตการสื่อสารพื้นฐานระหว่างการเรียกดูบริบทต่างๆ ผ่าน BroadcastChannel ออบเจ็กต์
ก่อนอื่น แต่ละบริบทต้องสร้างอินสแตนซ์ BroadcastChannel
ด้วยรหัสเดียวกันเพื่อใช้งาน
แล้วส่งและรับข้อความจากโปรแกรม:
const broadcast = new BroadcastChannel('channel-123');
ออบเจ็กต์ BroadcastChannel แสดงอินเทอร์เฟซ postMessage()
เพื่อส่งข้อความไปยังการฟังทั้งหมด
บริบท:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
ทุกบริบทของเบราว์เซอร์จะฟังข้อความผ่านเมธอด onmessage
ของ BroadcastChannel
ได้
ออบเจ็กต์:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
จากที่เห็น ไม่มีการอ้างอิงถึงบริบทใด ๆ อย่างชัดเจน จึงไม่จำเป็นต้องมีการอ้างอิง อ้างอิงให้กับโปรแกรมทำงานของบริการหรือไคลเอ็นต์ที่เฉพาะเจาะจงก่อน
ข้อเสียก็คือ ขณะที่เขียนข้อมูลนี้ API ได้รับการสนับสนุนจาก Chrome และ Firefox และ Edge แต่เบราว์เซอร์อื่นๆ เช่น Safari ไม่รองรับ ในตอนนี้
API ของไคลเอ็นต์
API ไคลเอ็นต์ช่วยให้คุณได้รับ
อ้างอิงถึงออบเจ็กต์ WindowClient
ทั้งหมดที่แสดงแท็บที่ใช้งานอยู่ซึ่ง Service Worker กำลังควบคุมอยู่
เนื่องจากหน้านี้ควบคุมโดย Service Worker เพียงตัวเดียว โปรแกรมจะรับคำสั่งและส่งข้อความไปยัง
Service Worker ที่ใช้งานอยู่โดยตรงผ่านอินเทอร์เฟซ serviceWorker
:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
ในทำนองเดียวกัน โปรแกรมทำงานของบริการจะฟังข้อความโดยใช้ Listener onmessage
ดังนี้
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
ในการสื่อสารกลับไปยังไคลเอ็นต์ โปรแกรมทำงานของบริการจะได้รับอาร์เรย์
WindowClient
ออบเจ็กต์โดยการดำเนินการ
วิธีการต่างๆ เช่น
Clients.matchAll()
และ
Clients.get()
จากนั้นจะสามารถ
postMessage()
รายใดก็ได้:
//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
if (clients && clients.length) {
//Respond to last focused tab
clients[0].postMessage({type: 'MSG_ID'});
}
});
Client API
เป็นตัวเลือกที่ดีในการสื่อสารกับแท็บที่ใช้งานอยู่ทั้งหมดจาก Service Worker ได้อย่างง่ายดาย
ในลักษณะที่ค่อนข้างตรงไปตรงมา API นี้ได้รับการสนับสนุนโดยผู้เผยแพร่โฆษณาหลักทั้งหมด
เบราว์เซอร์
แต่วิธีการบางอย่างอาจไม่พร้อมใช้งาน ดังนั้น โปรดตรวจสอบการสนับสนุนของเบราว์เซอร์ก่อน
ติดตั้งโค้ดนี้ในเว็บไซต์
ช่องทางของข้อความ
ต้องมีช่องทางข้อความ การกำหนดและส่งพอร์ตจากบริบทหนึ่งไปยังอีกบริบทหนึ่งเพื่อสร้างการสื่อสารแบบ2 ทาง
ในการเริ่มต้นแชแนล หน้าเว็บจะสร้างอินสแตนซ์ MessageChannel
และนำมาใช้
เพื่อส่งพอร์ตไปยัง Service Worker ที่ลงทะเบียนไว้ หน้านี้ยังติดตั้ง Listener onmessage
ใน
เพื่อรับข้อความจากบริบทอื่น:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Service Worker ได้รับพอร์ต บันทึกการอ้างอิง และใช้พอร์ตนั้นเพื่อส่งข้อความถึงอีกฝ่าย ด้าน:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
ขณะนี้ MessageChannel
ได้รับการสนับสนุนโดยผู้ให้บริการหลักทั้งหมด
เบราว์เซอร์
API ขั้นสูง: การซิงค์ในเบื้องหลังและการดึงข้อมูลในเบื้องหลัง
ในคู่มือนี้เราได้สำรวจวิธีใช้เทคนิคการสื่อสารแบบสองทาง โดย กรณีง่ายๆ เช่น การส่งข้อความสตริงที่อธิบายการดำเนินการที่ทำได้ หรือรายการ URL เพื่อแคชจากบริบทหนึ่งไปยังอีกบริบทหนึ่ง ในส่วนนี้ เราจะมาดู API ทั้ง 2 แบบเพื่อจัดการกับ สถานการณ์: ขาดการเชื่อมต่อและการดาวน์โหลดเป็นเวลานาน
การซิงค์ในเบื้องหลัง
แอปแชทอาจต้องการดูแลให้ข้อความต่างๆ ไม่สูญหายไปเนื่องจากการเชื่อมต่อที่ไม่ดี Background Sync API ช่วยให้คุณ เลื่อนการดำเนินการที่จะลองใหม่เมื่อผู้ใช้มีการเชื่อมต่อที่เสถียร ซึ่งมีประโยชน์ในการตรวจสอบว่า ข้อมูลใดก็ตามที่ผู้ใช้ต้องการจะส่ง จะส่งออกไป
หน้าเว็บจะบันทึก sync
แทนที่จะเป็นอินเทอร์เฟซ postMessage()
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
จากนั้น Service Worker จะคอยตรวจจับเหตุการณ์ sync
เพื่อประมวลผลข้อความ
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
ฟังก์ชัน doSomeStuff()
ควรจะแสดงคำมั่นสัญญาที่ระบุความสำเร็จ/ความล้มเหลวของสิ่งต่างๆ
พยายามจะทำอะไร หากเป็นไปตามเงื่อนไข การซิงค์ก็เสร็จสมบูรณ์ หากไม่สำเร็จ ระบบจะกำหนดเวลาซิงค์อีกครั้งเป็น
ลองอีกครั้ง ลองซิงค์อีกครั้งที่รอการเชื่อมต่อ และใช้ Exponential Backoff
เมื่อดำเนินการแล้ว โปรแกรมทำงานของบริการจะสามารถสื่อสารกลับไปยังหน้าเว็บเพื่อ อัปเดต UI โดยใช้ API การสื่อสารที่สำรวจก่อนหน้านี้
Google Search ใช้การซิงค์ในเบื้องหลังเพื่อเก็บการค้นหาที่ล้มเหลวเนื่องจากการเชื่อมต่อไม่ดี แล้วลองอีกครั้ง ภายหลังเมื่อผู้ใช้ออนไลน์ เมื่อดำเนินการแล้ว พวกเขาจะแจ้งผลลัพธ์กับ ผู้ใช้ผ่านทางข้อความ Push ในเว็บ
การดึงข้อมูลในเบื้องหลัง
สำหรับงานสั้นๆ เช่น การส่งข้อความ หรือรายการ URL ที่จะแคช ตัวเลือก ที่สำรวจจนถึงตอนนี้ก็เป็นทางเลือกที่ดี หากงานใช้เวลานานเกินไป เบราว์เซอร์จะหยุดบริการ มิฉะนั้นจะเกิดความเสี่ยงต่อความเป็นส่วนตัวและแบตเตอรี่ของผู้ใช้
API การดึงข้อมูลในเบื้องหลัง ช่วยให้คุณย้ายงานที่ใช้เวลานานไปยังโปรแกรมทำงานของบริการ เช่น การดาวน์โหลดภาพยนตร์ พอดแคสต์ หรือระดับต่างๆ ของเกม
ในการสื่อสารกับ Service Worker จากหน้าเว็บ ให้ใช้ backgroundFetch.fetch
แทน
postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
ออบเจ็กต์ BackgroundFetchRegistration
อนุญาตให้หน้าเว็บตอบสนองต่อเหตุการณ์ progress
ตามมา
ความคืบหน้าของการดาวน์โหลด:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});
ขั้นตอนถัดไป
ในคู่มือนี้ เราศึกษากรณีทั่วไปที่สุดของการสื่อสารระหว่างเพจกับโปรแกรมทำงานของบริการ (การสื่อสารแบบ 2 ทิศทาง)
บ่อยครั้ง ผู้ใช้คนหนึ่งอาจต้องใช้เพียงบริบทเดียวเพื่อสื่อสารกับอีกฝ่าย โดยไม่ได้รับ คำตอบ ดูคำแนะนำต่อไปนี้สําหรับคําแนะนําเกี่ยวกับวิธีใช้เทคนิคแบบทิศทางเดียวใน หน้าเว็บของคุณจากและไปยัง Service Worker พร้อมทั้งกรณีการใช้งานและตัวอย่างเวอร์ชันที่ใช้งานจริง
- คู่มือการแคชที่สำคัญ: การเรียกใช้ Service Worker จากหน้าเว็บเพื่อ แคชทรัพยากรล่วงหน้า (เช่น ในสถานการณ์การดึงข้อมูลล่วงหน้า)
- อัปเดตการออกอากาศ: เรียกใช้หน้าเว็บจาก Service Worker เพื่อแจ้ง เกี่ยวกับอัปเดตที่สำคัญ (เช่น เว็บแอปเวอร์ชันใหม่พร้อมใช้งาน)