در برخی موارد، ممکن است برنامه وب نیاز به ایجاد کانال ارتباطی دوطرفه بین صفحه و کارگر سرویس داشته باشد.
برای مثال: در یک برنامه وب پیشرونده پادکست، میتوان ویژگیای ساخت که به کاربر اجازه دهد قسمتها را برای استفاده آفلاین بارگیری کند و به کارگر سرویس اجازه دهد تا صفحه را بهطور منظم از پیشرفت مطلع کند، تا رشته اصلی بتواند واسط کاربر را بهروز کند.
در این راهنما، روشهای مختلف پیادهسازی ارتباط دوطرفه بین زمینه پنجره و سرویس ورکر را با بررسی میاناهای برنامهسازی کاربردی مختلف، کتابخانه Workbox، و همچنین برخی موارد پیشرفته بررسی میکنیم.
استفاده از Workbox
workbox-window مجموعهای از
ماژولهای کتابخانه Workbox است که برای
اجرا در بافتار پنجره درنظر گرفته شدهاند. کلاس Workbox
روش messageSW() را برای ارسال پیام به سرویسکار ثبتشده نمونه و
منتظر ماندن برای پاسخ ارائه میدهد.
کد صفحه زیر یک نمونه Workbox جدید ایجاد میکند و پیامی به کارگر سرویس ارسال میکند
تا نسخه آن را دریافت کند:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
سرویسکار یک شنونده پیام را در طرف دیگر پیادهسازی میکند و به سرویسکار ثبتشده پاسخ میدهد:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
در پشت صحنه، این کتابخانه از یک API مرورگر استفاده میکند که در بخش بعدی آن را بررسی خواهیم کرد: کانال پیام، اما بسیاری از جزئیات پیادهسازی را انتزاعی میکند و استفاده از آن را آسانتر میسازد، درحالیکه از پشتیبانی گسترده مرورگر که این API دارد بهره میبرد.
استفاده از میاناهای برنامهسازی کاربردی مرورگر
اگر کتابخانه Workbox برای نیازهای شما کافی نیست، چندین میانای برنامهسازی کاربردی سطح پایینتر برای پیادهسازی ارتباط «دو طرفه» بین صفحهها و کارگران خدمات دردسترس است. آنها شباهتها و تفاوتهایی دارند:
شباهتها:
- در همه موارد، ارتباط از یک طرف ازطریق میانای
postMessage()شروع میشود و در طرف دیگر با پیادهسازی یک گردانندهmessageدریافت میشود. - در عمل، همه میاناهای برنامهسازی کاربردی دردسترس به ما امکان میدهند موارد استفاده یکسانی را پیادهسازی کنیم، اما برخیاز آنها ممکن است توسعه را در برخی سناریوها سادهتر کنند.
تفاوتها:
- آنها روشهای مختلفی برای شناسایی طرف دیگر ارتباط دارند: برخیاز آنها از ارجاع صریح به بافت دیگر استفاده میکنند، درحالیکه برخی دیگر میتوانند بهطور ضمنی ازطریق شیء پراکسی که در هر طرف نمونهسازی شده است ارتباط برقرار کنند.
- پشتیبانی مرورگر در آنها متفاوت است.
Broadcast Channel API
Broadcast Channel API امکان برقراری ارتباط پایه بین زمینههای مرور را ازطریق BroadcastChannel objects فراهم میکند.
برای پیادهسازی آن، ابتدا هر بافت باید یک شیء 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...
}
};
همانطور که میبینید، هیچ ارجاع صریحی به بافت خاصی وجود ندارد، بنابراین نیازی به دریافت ارجاع به عامل خدماتی یا هر مشتری خاصی نیست.
نکته منفی این است که در زمان نوشتن این مطلب، این میانای برنامهسازی کاربردی از Chrome، Firefox، و Edge پشتیبانی میکند، اما مرورگرهای دیگر، مثل Safari، هنوز از آن پشتیبانی نمیکنند.
میانای برنامهسازی کاربردی کارخواه
Client API به شما امکان میدهد
مرجعی برای همه اشیاء WindowClient که نشاندهنده برگههای فعالی هستند که کارگر سرویس کنترل میکند دریافت کنید.
ازآنجاییکه صفحه توسط یک عامل خدماتی کنترل میشود، مستقیماً ازطریق میانای 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
}
};
بهطور مشابه، کارگر سرویس با پیادهسازی شنونده onmessage به پیامها گوش میدهد:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
برای برقراری ارتباط با هریک از کارخواهان، کارمند خدمات با اجرای روشهایی مثل
Clients.matchAll() و
Clients.get()،
آرایهای از اشیاء
WindowClient را بهدست میآورد. سپس میتواند
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 is a good option to communicate easily with all the active tabs from a service worker
in a relatively straightforward way. این میانای برنامهسازی کاربردی توسط همه مرورگرهای اصلی پشتیبانی میشود، اما ممکن است همه روشهای آن دردسترس نباشد، بنابراین حتماً قبلاز پیادهسازی آن در سایتتان، پشتیبانی مرورگر را بررسی کنید.
کانال پیام
کانال پیام برای ایجاد کانال ارتباطی دوطرفه، به تعریف و ارسال درگاه از یک زمینه به زمینه دیگر نیاز دارد.
برای مقداردهی اولیه کانال، صفحه یک شیء MessageChannel را نمونهسازی میکند و از آن برای ارسال درگاه به کارگر سرویس ثبتشده استفاده میکند. صفحه همچنین یک شنونده 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
};
سرویسکار درگاه را دریافت میکند، ارجاعی به آن ذخیره میکند، و از آن برای ارسال پیام به طرف دیگر استفاده میکند:
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 از همه مرورگرهای
اصلی پشتیبانی میکند.
میاناهای برنامهسازی کاربردی پیشرفته: همگامسازی پسزمینه و واکشی پسزمینه
در این راهنما، روشهای پیادهسازی تکنیکهای ارتباطی دوطرفه را برای موارد نسبتاً ساده بررسی کردیم، مانند ارسال یک پیام رشتهای که عملیات اجرایی را توصیف میکند، یا فهرستی از نشانیهای وب برای ذخیره در حافظه پنهان از یک زمینه به زمینه دیگر. در این بخش، دو میانای برنامهسازی کاربردی را برای رسیدگی به سناریوهای خاص بررسی خواهیم کرد: عدم اتصال و بارگیریهای طولانی.
همگامسازی پسزمینه
برنامه گپ ممکن است بخواهد مطمئن شود که پیامها هرگز بهدلیل اتصال بد ازبین نمیروند. Background Sync API به شما امکان میدهد کنشها را بهتعویق بیندازید تا وقتی کاربر اتصال پایداری دارد دوباره امتحان شوند. این کار برای اطمینان از اینکه هرچه کاربر میخواهد ارسال کند واقعاً ارسال میشود مفید است.
بهجای میانای postMessage()، صفحه sync را ثبت میکند:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
سپس کارگر سرویس به رویداد sync گوش میدهد تا پیام را پردازش کند:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
تابع doSomeStuff() باید یک promise برگرداند که نشاندهنده موفقیت/شکست هر کاری است که تلاش میکند انجام دهد. اگر این promise برآورده شود، همگامسازی کامل است. اگر ناموفق باشد، همگامسازی دیگری برای امتحان مجدد زمانبندی خواهد شد. همگامسازیهای مجدد نیز منتظر اتصال میمانند و از عقبنشینی نمایی استفاده میکنند.
پساز انجام عملیات، عامل خدماتی میتواند بااستفاده از هرکدام از میاناهای برنامهسازی کاربردی ارتباطی که قبلاً بررسی شد، با صفحه ارتباط برقرار کند تا واسط کاربر را بهروز کند.
«جستجوی Google» از «همگامسازی پسزمینه» برای حفظ پُرسمانهای ناموفق بهدلیل اتصال بد استفاده میکند و بعداً وقتی کاربر آنلاین است، آنها را دوباره امتحان میکند. وقتی عملیات انجام شد، نتیجه را ازطریق اعلان لحظهای وب به کاربر اطلاع میدهند:
واکشی پسزمینه
برای کارهای نسبتاً کوتاه مانند ارسال پیام یا فهرستی از نشانیهای وب برای ذخیره در حافظه پنهان، گزینههایی که تاکنون بررسی شدهاند انتخاب خوبی هستند. اگر تکلیف خیلی طول بکشد، مرورگر کارگر سرویس را ازبین میبرد، درغیراینصورت حریم خصوصی و باتری کاربر بهخطر میافتد.
Background Fetch API به شما امکان میدهد کارهای طولانی را به یک سرویسکار واگذار کنید، مانند بارگیری فیلم، پادکست، یا مراحل یک بازی.
برای برقراری ارتباط با کارمند خدمات از صفحه، بهجای postMessage() از backgroundFetch.fetch استفاده کنید:
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}%`);
});
مراحل بعدی
در این راهنما، عمومیترین حالت ارتباط بین صفحه و سرویسکار را بررسی کردیم (ارتباط دوطرفه).
در بسیاری از مواقع، ممکن است فرد فقط به یک زمینه برای برقراری ارتباط با دیگری نیاز داشته باشد، بدون اینکه پاسخی دریافت کند. برای راهنمایی درباره نحوه پیادهسازی تکنیکهای یکسویه در صفحههایتان از و به سرویسکار، همراه با موارد استفاده و نمونههای تولید، راهنماهای زیر را بررسی کنید:
- راهنمای ذخیره در حافظه نهان ضروری: فراخوانی عامل خدماتی از صفحه برای ذخیره کردن منابع در حافظه نهان ازقبل (مثلاً در سناریوهای پیشواکشی).
- بهروزرسانیهای همهفرستی: فراخوانی صفحه از کارگر سرویس برای اطلاعرسانی درباره بهروزرسانیهای مهم (مثلاً نسخه جدیدی از برنامه وب دردسترس است).