ارتباط دو طرفه با کارکنان خدمات

اندرو گوان
Andrew Guan

در برخی موارد، یک برنامه وب ممکن است نیاز به ایجاد یک کانال ارتباطی دو طرفه بین صفحه و کارمند سرویس داشته باشد.

به عنوان مثال: در یک پادکست 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 استفاده می‌کند.

نموداری که ارتباط دو طرفه بین صفحه و کارگر سرویس را با استفاده از پنجره Workbox نشان می دهد.

استفاده از API های مرورگر

اگر کتابخانه Workbox برای نیازهای شما کافی نیست، چندین API سطح پایین‌تر برای پیاده‌سازی ارتباط «دو طرفه» بین صفحات و کارکنان خدمات وجود دارد. آنها شباهت ها و تفاوت هایی دارند:

شباهت ها:

  • در همه موارد ارتباط از یک طرف از طریق واسط postMessage() شروع می شود و از طرف دیگر با پیاده سازی یک کنترل کننده message دریافت می شود.
  • در عمل، همه API های موجود به ما اجازه می دهند تا موارد استفاده یکسانی را پیاده سازی کنیم، اما برخی از آنها ممکن است توسعه را در برخی سناریوها ساده کنند.

تفاوت ها:

  • آنها راه‌های مختلفی برای شناسایی طرف دیگر ارتباط دارند: برخی از آنها از یک ارجاع صریح به زمینه دیگر استفاده می‌کنند، در حالی که برخی دیگر می‌توانند به طور ضمنی از طریق یک شی پراکسی که در هر طرف ایجاد شده است، ارتباط برقرار کنند.
  • پشتیبانی مرورگرها در بین آنها متفاوت است.
نموداری که ارتباط دو طرفه بین صفحه و کارمند سرویس و APIهای مرورگر موجود را نشان می دهد.

Broadcast Channel API

پشتیبانی مرورگر

  • کروم: 54.
  • لبه: 79.
  • فایرفاکس: 38.
  • سافاری: 15.4.

منبع

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

پشتیبانی مرورگر

  • کروم: 40.
  • لبه: 17.
  • فایرفاکس: 44.
  • سافاری: 11.1.

منبع

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 توسط همه مرورگرهای اصلی پشتیبانی می‌شود، اما ممکن است همه روش‌های آن در دسترس نباشد، بنابراین قبل از اجرای آن در سایت خود، حتماً پشتیبانی مرورگر را بررسی کنید.

کانال پیام

پشتیبانی مرورگر

  • کروم: 2.
  • لبه: 12.
  • فایرفاکس: 41.
  • سافاری: 5.

منبع

کانال پیام برای ایجاد یک کانال ارتباطی دو طرفه نیاز به تعریف و عبور یک پورت از یک زمینه به متن دیگر دارد.

برای مقداردهی اولیه کانال، صفحه یک شی 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 را برای رسیدگی به سناریوهای خاص بررسی خواهیم کرد: عدم اتصال و دانلود طولانی.

همگام سازی پس زمینه

پشتیبانی مرورگر

  • کروم: 49.
  • لبه: 79.
  • فایرفاکس: پشتیبانی نمی شود.
  • سافاری: پشتیبانی نمی شود.

منبع

یک برنامه چت ممکن است بخواهد مطمئن شود که پیام ها به دلیل اتصال بد هرگز از بین نمی روند. 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 از همگام‌سازی پس‌زمینه برای تداوم درخواست‌های ناموفق به دلیل اتصال بد استفاده می‌کند و بعداً زمانی که کاربر آنلاین است دوباره آنها را امتحان کنید. پس از انجام عملیات، آنها نتیجه را از طریق یک اعلان فشار وب به کاربر اطلاع می دهند:

نموداری که صفحه ای را نشان می دهد که یک پورت را به یک سرویس دهنده منتقل می کند تا ارتباط دو طرفه برقرار کند.

واکشی پس‌زمینه

پشتیبانی مرورگر

  • کروم: 74.
  • لبه: 79.
  • فایرفاکس: پشتیبانی نمی شود.
  • سافاری: پشتیبانی نمی شود.

منبع

برای کارهای نسبتاً کوتاه مانند ارسال یک پیام، یا لیستی از 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}%`);
});
نموداری که صفحه ای را نشان می دهد که یک پورت را به یک سرویس دهنده منتقل می کند تا ارتباط دو طرفه برقرار کند.
رابط کاربری برای نشان دادن پیشرفت دانلود (سمت چپ) به روز می شود. با تشکر از کارگران خدمات، عملیات می‌تواند زمانی که همه برگه‌ها بسته شده‌اند به اجرا ادامه دهد (راست).

مراحل بعدی

در این راهنما، کلی‌ترین حالت ارتباط بین کارکنان صفحه و خدمات (ارتباط دو طرفه) را بررسی کردیم.

بسیاری از اوقات، ممکن است یکی برای برقراری ارتباط با دیگری، بدون دریافت پاسخ، تنها به یک زمینه نیاز داشته باشد. راهنماهای زیر را برای راهنمایی نحوه پیاده‌سازی تکنیک‌های یک طرفه در صفحات خود از و به کارگر سرویس، همراه با موارد استفاده و نمونه‌های تولید، بررسی کنید:

  • راهنمای ذخیره سازی ضروری : فراخوانی یک سرویس دهنده از صفحه برای ذخیره منابع از قبل (به عنوان مثال در سناریوهای واکشی اولیه).
  • به‌روزرسانی‌های پخش : تماس با صفحه از طرف سرویس‌دهنده برای اطلاع از به‌روزرسانی‌های مهم (مثلاً نسخه جدیدی از برنامه وب موجود است).