Service Worker'larla iki yönlü iletişim

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Bazı durumlarda, web uygulamalarının sayfa ile hizmet çalışanı arasında iki yönlü bir iletişim kanalı oluşturması gerekebilir.

Örneğin, bir podcast PWA'sında, kullanıcının bölümleri çevrimdışı tüketim için indirmesine olanak tanıyan bir özellik oluşturabilir ve hizmet çalışanının, ilerleme durumu hakkında sayfayı düzenli olarak bilgilendirmesine izin verebilirsiniz. Böylece ana iş parçacığı, kullanıcı arayüzünü güncelleyebilir.

Bu kılavuzda, farklı API'leri, Workbox kitaplığını ve bazı ileri düzey kullanım alanlarını inceleyerek Window ile hizmet çalışanı bağlamı arasında çift yönlü iletişim kurmanın farklı yollarını keşfedeceğiz.

Hizmet çalışanını ve mesaj alışverişi yapan sayfayı gösteren diyagram.

Workbox'ı kullanma

workbox-window, Workbox kitaplığının pencere bağlamında çalıştırılması amaçlanan bir dizi modülüdür. Workbox sınıfı, örneğin kayıtlı hizmet çalışanına mesaj göndermek ve yanıt beklemek için bir messageSW() yöntemi sağlar.

Aşağıdaki sayfa kodu yeni bir Workbox örneği oluşturur ve sürümünü almak için hizmet çalışanına bir mesaj gönderir:

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

Hizmet çalışanı, diğer uçta bir mesaj dinleyici uygular ve kayıtlı hizmet çalışanına yanıt verir:

const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

Kitaplık, arka planda bir sonraki bölümde inceleyeceğimiz bir tarayıcı API'si olan Message Channel'ı kullanır ancak birçok uygulama ayrıntısını soyutlayarak kullanımı kolaylaştırır ve bu API'nin sunduğu geniş tarayıcı desteğinden yararlanır.

Workbox Window kullanılarak sayfa ile hizmet çalışanı arasındaki iki yönlü iletişimi gösteren diyagram.

Tarayıcı API'lerini kullanma

Workbox kitaplığı ihtiyaçlarınız için yeterli değilse sayfalar ve hizmet çalışanları arasında "iki yönlü" iletişim uygulamak için kullanabileceğiniz birkaç alt düzey API vardır. Bazı benzerlikleri ve farklılıkları vardır:

Benzerlikler:

  • Tüm durumlarda iletişim, bir uçta postMessage() arayüzü aracılığıyla başlar ve diğer uçta bir message işleyici uygulanarak alınır.
  • Pratikte, mevcut tüm API'ler aynı kullanım alanlarını uygulamamıza olanak tanır ancak bazıları bazı senaryolarda geliştirmeyi basitleştirebilir.

Farklılıklar:

  • İletişimin diğer tarafını tanımlamak için farklı yöntemleri vardır: Bazıları diğer bağlama açık bir referans kullanırken, diğerleri her iki tarafta örneklendirilen bir proxy nesnesi aracılığıyla dolaylı olarak iletişim kurabilir.
  • Tarayıcı desteği, bu yöntemler arasında farklılık gösterir.
Sayfa ile hizmet çalışanı arasındaki iki yönlü iletişimi ve kullanılabilir tarayıcı API'lerini gösteren şema.

Broadcast Channel API

Browser Support

  • Chrome: 54.
  • Edge: 79.
  • Firefox: 38.
  • Safari: 15.4.

Source

Broadcast Channel API, BroadcastChannel nesneleri aracılığıyla tarama bağlamları arasında temel iletişime olanak tanır.

Bunu uygulamak için öncelikle her bağlamın aynı kimliğe sahip bir BroadcastChannel nesnesini örneklendirmesi ve bu nesneden mesaj gönderip alması gerekir:

const broadcast = new BroadcastChannel('channel-123');

BroadcastChannel nesnesi, dinleyen herhangi bir bağlama mesaj göndermek için bir postMessage() arayüzü sunar:

//send message
broadcast.postMessage({ type: 'MSG_ID', });

Herhangi bir tarayıcı bağlamı, BroadcastChannel nesnesinin onmessage yöntemi aracılığıyla mesajları dinleyebilir:

//listen to messages
broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process message...
  }
};

Gördüğünüz gibi, belirli bir bağlama açık bir referans yoktur. Bu nedenle, önce hizmet çalışanına veya belirli bir istemciye referans almanıza gerek yoktur.

Broadcast Channel nesnesi kullanılarak sayfa ile hizmet çalışanı arasındaki iki yönlü iletişimi gösteren diyagram.

Dezavantajı ise bu makalenin yazıldığı tarih itibarıyla API'nin Chrome, Firefox ve Edge tarafından desteklenmesi ancak Safari gibi diğer tarayıcılar tarafından henüz desteklenmemesidir.

Client API

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

Client API, hizmet çalışanının kontrol ettiği etkin sekmeleri temsil eden tüm WindowClient nesnelerine referans almanıza olanak tanır.

Sayfa tek bir hizmet çalışanı tarafından kontrol edildiğinden, doğrudan serviceWorker arayüzü üzerinden etkin hizmet çalışanını dinler ve ona mesaj gönderir:

//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
  }
};

Benzer şekilde, hizmet çalışanı bir onmessage dinleyici uygulayarak mesajları dinler:

//listen to messages
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //Process message
  }
});

Hizmet çalışanı, istemcilerinden herhangi biriyle tekrar iletişim kurmak için Clients.matchAll() ve Clients.get() gibi yöntemleri yürüterek bir WindowClient nesnesi dizisi elde eder. Ardından, bunlardan herhangi birini 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'});
  }
});
Bir dizi istemciyle iletişim kuran hizmet çalışanını gösteren diyagram.

Client API, bir hizmet çalışanından tüm etkin sekmelerle nispeten basit bir şekilde kolayca iletişim kurmak için iyi bir seçenektir. API, tüm önemli tarayıcılar tarafından desteklenir ancak tüm yöntemleri kullanılamayabilir. Bu nedenle, sitenizde uygulamadan önce tarayıcı desteğini kontrol edin.

Mesaj Kanalı

Browser Support

  • Chrome: 2.
  • Edge: 12.
  • Firefox: 41.
  • Safari: 5.

Source

Message Channel, iki yönlü bir iletişim kanalı oluşturmak için bir bağlantı noktasının tanımlanmasını ve bir bağlamdan diğerine aktarılmasını gerektirir.

Sayfa, kanalı ilk kullanıma hazırlamak için bir MessageChannel nesnesi örneklendirir ve kayıtlı hizmet çalışanına bağlantı noktası göndermek için bu nesneyi kullanır. Sayfada, diğer bağlamdan mesaj almak için bir onmessage işleyici de uygulanır:

const messageChannel = new MessageChannel();

//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

//Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};
İki yönlü iletişim kurmak için bir hizmet çalışanına bağlantı noktası ileten bir sayfayı gösteren diyagram.

Hizmet çalışanı, bağlantı noktasını alır, bağlantı noktasına bir referans kaydeder ve diğer tarafa mesaj göndermek için bu referansı kullanır:

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 şu anda tüm büyük tarayıcılar tarafından desteklenmektedir.

Gelişmiş API'ler: Arka Plan Senkronizasyonu ve Arka Plan Getirme

Bu kılavuzda, nispeten basit durumlarda iki yönlü iletişim tekniklerini uygulamanın yollarını inceledik. Örneğin, gerçekleştirilecek işlemi açıklayan bir dize mesajı veya bir bağlamdan diğerine önbelleğe alınacak URL'lerin listesini iletme. Bu bölümde, bağlantı olmaması ve uzun indirme süreleri gibi belirli senaryoları ele almak için iki API'yi inceleyeceğiz.

Arka Plan Senkronizasyonu

Browser Support

  • Chrome: 49.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

Bir sohbet uygulaması, kötü bağlantı nedeniyle mesajların kaybolmamasını sağlamak isteyebilir. Background Sync API, kullanıcının bağlantısı stabil olduğunda yeniden denenecek işlemleri ertelemenize olanak tanır. Bu, kullanıcının göndermek istediği her şeyin gerçekten gönderilmesini sağlamak için kullanışlıdır.

Sayfa, postMessage() arayüzü yerine bir sync kaydeder:

navigator.serviceWorker.ready.then(function (swRegistration) {
  return swRegistration.sync.register('myFirstSync');
});

Ardından hizmet çalışanı, mesajı işlemek için sync etkinliğini dinler:

self.addEventListener('sync', function (event) {
  if (event.tag == 'myFirstSync') {
    event.waitUntil(doSomeStuff());
  }
});

doSomeStuff() işlevi, yapmaya çalıştığı işlemin başarılı/başarısız olduğunu belirten bir Promise döndürmelidir. İşlev yerine getirilirse senkronizasyon tamamlanır. Başarısız olursa yeniden denemek için başka bir senkronizasyon planlanır. Yeniden deneme senkronizasyonları da bağlantı kurulmasını bekler ve eksponansiyel geri yükleme kullanır.

İşlem gerçekleştirildikten sonra hizmet çalışanı, daha önce incelenen iletişim API'lerinden herhangi birini kullanarak kullanıcı arayüzünü güncellemek için sayfayla tekrar iletişim kurabilir.

Google Arama, zayıf bağlantı nedeniyle başarısız olan sorguları saklamak ve kullanıcı internete bağlandığında tekrar denemek için Arka Planda Senkronizasyon'u kullanır. İşlem gerçekleştirildikten sonra, sonucu bir web push bildirimi aracılığıyla kullanıcıya iletirler:

İki yönlü iletişim kurmak için bir hizmet çalışanına bağlantı noktası ileten bir sayfayı gösteren diyagram.

Arka planda getirme

Browser Support

  • Chrome: 74.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

Mesaj gönderme veya önbelleğe alınacak URL'lerin listesi gibi nispeten kısa işler için şu ana kadar incelenen seçenekler iyi bir tercihtir. Görev çok uzun sürerse tarayıcı, hizmet çalışanını sonlandırır. Aksi takdirde, kullanıcının gizliliği ve pili riske girer.

Background Fetch API, film, podcast veya oyun seviyeleri indirme gibi uzun süren görevleri bir hizmet çalışanına devretmenize olanak tanır.

Sayfadan hizmet çalışanına mesaj göndermek için postMessage() yerine backgroundFetch.fetch kullanın:

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 nesnesi, sayfanın progress etkinliğini dinleyerek indirme işleminin ilerleme durumunu takip etmesine olanak tanır:

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}%`);
});
İki yönlü iletişim kurmak için bir hizmet çalışanına bağlantı noktası ileten bir sayfayı gösteren diyagram.
Kullanıcı arayüzü, indirme işleminin ilerleme durumunu gösterecek şekilde güncellendi (sol). Hizmet çalışanları sayesinde tüm sekmeler kapatıldığında işlem çalışmaya devam edebilir (sağda).

Sonraki adımlar

Bu kılavuzda, sayfa ile hizmet çalışanları arasındaki en genel iletişim durumunu (çift yönlü iletişim) inceledik.

Çoğu zaman, bir tarafın yanıt almadan diğer tarafla iletişim kurması için tek bir bağlam yeterli olabilir. Sayfalarınızda hizmet çalışanına ve hizmet çalışanından tek yönlü teknikleri nasıl uygulayacağınızla ilgili rehberlik, kullanım alanları ve üretim örnekleri için aşağıdaki kılavuzlara göz atın:

  • Zorunlu önbelleğe alma kılavuzu: Kaynakları önceden önbelleğe almak için sayfadan bir hizmet çalışanını çağırma (ör. önceden getirme senaryolarında).
  • Güncellemeleri yayınlama: Önemli güncellemeleri (ör. web uygulamasının yeni bir sürümü mevcut) bildirmek için hizmet çalışanından sayfayı çağırma.