در برخی موارد، یک برنامه وب ممکن است نیاز به ایجاد یک کانال ارتباطی دو طرفه بین صفحه و کارمند سرویس داشته باشد.
به عنوان مثال: در یک پادکست PWA میتوان ویژگیای ایجاد کرد که به کاربر اجازه میدهد قسمتها را برای مصرف آفلاین بارگیری کند و به کارگر سرویس اجازه دهد تا صفحه را به طور مرتب از پیشرفت مطلع کند، بنابراین موضوع اصلی میتواند رابط کاربری را بهروزرسانی کند.
در این راهنما، با کاوش در APIهای مختلف، کتابخانه Workbox و همچنین برخی موارد پیشرفته، راههای مختلف اجرای یک ارتباط دو طرفه بین زمینه Window و Service Worker را بررسی خواهیم کرد.
با استفاده از 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 استفاده میکند.
استفاده از API های مرورگر
اگر کتابخانه Workbox برای نیازهای شما کافی نیست، چندین API سطح پایینتر برای پیادهسازی ارتباط «دو طرفه» بین صفحات و کارکنان خدمات وجود دارد. آنها شباهت ها و تفاوت هایی دارند:
شباهت ها:
- در همه موارد ارتباط از یک طرف از طریق واسط
postMessage()
شروع می شود و از طرف دیگر با پیاده سازی یک کنترل کنندهmessage
دریافت می شود. - در عمل، همه API های موجود به ما اجازه می دهند تا موارد استفاده یکسانی را پیاده سازی کنیم، اما برخی از آنها ممکن است توسعه را در برخی سناریوها ساده کنند.
تفاوت ها:
- آنها راههای مختلفی برای شناسایی طرف دیگر ارتباط دارند: برخی از آنها از یک ارجاع صریح به زمینه دیگر استفاده میکنند، در حالی که برخی دیگر میتوانند به طور ضمنی از طریق یک شی پراکسی که در هر طرف ایجاد شده است، ارتباط برقرار کنند.
- پشتیبانی مرورگر در بین آنها متفاوت است.
Broadcast Channel 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 از کروم، فایرفاکس و اج پشتیبانی میکند، اما مرورگرهای دیگر، مانند سافاری، هنوز از آن پشتیبانی نمیکنند .
Client API
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
}
});
برای برقراری ارتباط مجدد با هر یک از مشتریان خود، سرویسکار آرایهای از اشیاء 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
گزینه خوبی برای برقراری ارتباط آسان با تمام برگه های فعال یک سرویس دهنده به روشی نسبتاً ساده است. API توسط همه مرورگرهای اصلی پشتیبانی میشود، اما ممکن است همه روشهای آن در دسترس نباشد، بنابراین قبل از اجرای آن در سایت خود، حتماً پشتیبانی مرورگر را بررسی کنید.
کانال پیام
کانال پیام برای ایجاد یک کانال ارتباطی دو طرفه نیاز به تعریف و عبور یک پورت از یک زمینه به متن دیگر دارد.
برای مقداردهی اولیه کانال، صفحه یک شی 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
در حال حاضر توسط همه مرورگرهای اصلی پشتیبانی می شود.
APIهای پیشرفته: همگامسازی پسزمینه و واکشی پسزمینه
در این راهنما، روشهای پیادهسازی تکنیکهای ارتباط دو طرفه را برای موارد نسبتاً ساده، مانند ارسال یک پیام رشتهای که عملیات را برای انجام توصیف میکند، یا فهرستی از URLها برای ذخیرهسازی از یک زمینه به متن دیگر، بررسی کردیم. در این بخش ما دو API را برای رسیدگی به سناریوهای خاص بررسی خواهیم کرد: عدم اتصال و دانلود طولانی.
همگام سازی پس زمینه
یک برنامه چت ممکن است بخواهد مطمئن شود که پیام ها به دلیل اتصال بد هرگز از بین نمی روند. 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()
باید وعده ای را برگرداند که نشان دهنده موفقیت/شکست هر کاری است که می خواهد انجام دهد. اگر انجام شود، همگام سازی کامل شده است. اگر ناموفق بود، همگامسازی دیگری برای امتحان مجدد برنامهریزی میشود. سعی مجدد همگامسازیها نیز برای اتصال منتظر میمانند و از یک عقبنشینی نمایی استفاده میکنند.
هنگامی که عملیات انجام شد، سرویسکار میتواند با استفاده از هر یک از APIهای ارتباطی که قبلاً بررسی شدهاند، دوباره با صفحه ارتباط برقرار کند تا UI را بهروزرسانی کند.
جستجوی Google از همگامسازی پسزمینه برای تداوم درخواستهای ناموفق به دلیل اتصال بد استفاده میکند و بعداً زمانی که کاربر آنلاین است دوباره آنها را امتحان کنید. پس از انجام عملیات، آنها نتیجه را از طریق یک اعلان فشار وب به کاربر اطلاع می دهند:
واکشی پسزمینه
برای کارهای نسبتاً کوتاه مانند ارسال یک پیام، یا لیستی از URL ها برای ذخیره، گزینه های بررسی شده تا کنون انتخاب خوبی هستند. اگر این کار بیش از حد طول بکشد، مرورگر کارمند سرویس را می کشد، در غیر این صورت خطری برای حریم خصوصی و باتری کاربر است.
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}%`);
});
مراحل بعدی
در این راهنما، کلیترین حالت ارتباط بین کارکنان صفحه و خدمات (ارتباط دو طرفه) را بررسی کردیم.
بسیاری از اوقات، ممکن است یکی برای برقراری ارتباط با دیگری، بدون دریافت پاسخ، تنها به یک زمینه نیاز داشته باشد. راهنماهای زیر را برای راهنمایی نحوه پیادهسازی تکنیکهای یک طرفه در صفحات خود از و به کارگر سرویس، همراه با موارد استفاده و نمونههای تولید، بررسی کنید:
- راهنمای ذخیره سازی ضروری : فراخوانی یک سرویس دهنده از صفحه برای ذخیره منابع از قبل (به عنوان مثال در سناریوهای واکشی اولیه).
- بهروزرسانیهای پخش : تماس با صفحه از طرف سرویسدهنده برای اطلاع از بهروزرسانیهای مهم (مثلاً نسخه جدیدی از برنامه وب موجود است).