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

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

Bazı durumlarda, bir web uygulaması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ışı izlemek için indirmesine ve hizmet çalışanının sayfayı ilerleme durumu hakkında düzenli olarak bilgilendirmesine izin veren bir özellik oluşturabilirsiniz. Böylece ana iş akışı kullanıcı arayüzünü güncelleyebilir.

Bu kılavuzda, farklı API'leri, Workbox kitaplığını ve bazı gelişmiş örnekleri inceleyerek pencere ile hizmet işleyici bağlamı arasında iki yönlü iletişimi uygulamanın farklı yollarını keşfedeceğiz.

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

Workbox'u kullanma

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

Aşağıdaki sayfa kodu yeni bir Workbox örneği oluşturur ve hizmet çalışanına sürümünü almak için 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);

Servis çalışanı, diğer tarafta bir mesaj dinleyici uygular ve kayıtlı servis ç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 tarayıcı API'si kullanır. Bu API'yi sonraki bölümde inceleyeceğiz: MessageChannel. Ancak kitaplık, birçok uygulama ayrıntısını soyutlayarak bu API'nin sunduğu geniş tarayıcı desteğinden yararlanırken kullanımını kolaylaştırır.

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

Tarayıcı API'lerini kullanma

Workbox kitaplığı ihtiyaçlarınız için yeterli değilse sayfalar ile hizmet çalışanları arasında "iki yönlü" iletişimi uygulamak için kullanılabilecek birkaç alt düzey API vardır. Bu iki yöntemin bazı benzerlikleri ve farklılıkları vardır:

Benzerlikler:

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

Farklılıklar:

  • İletişimin diğer tarafını tanımlamanın farklı yolları vardır: Bazıları diğer bağlama açık referans verir, bazıları ise her iki tarafta da oluşturulan bir proxy nesnesi aracılığıyla dolaylı olarak iletişim kurabilir.
  • Tarayıcı desteği, bu sürümler arasında değişiklik gösterir.
Sayfa ile hizmet çalışanı ve mevcut tarayıcı API'leri arasındaki iki yönlü iletişimi gösteren şema.

Broadcast Channel API

Tarayıcı desteği

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

Kaynak

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 nesnesi oluşturması ve bu nesne üzerinden ileti 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', });

Tüm tarayıcı bağlamları, 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örüldüğü gibi, belirli bir bağlama açık bir referans verilmemiştir. Bu nedenle, önce hizmet çalışanına veya belirli bir müşteriye referans almanız gerekmez.

Yayın kanalı nesnesi kullanılarak sayfa ile hizmet çalışanı arasındaki iki yönlü iletişimi gösteren şema.

Dezavantajı, bu makalenin yazıldığı sırada API'nin Chrome, Firefox ve Edge tarafından desteklenmesi ancak Safari gibi diğer tarayıcıların henüz desteklememesidir.

Client API

Tarayıcı desteği

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

Kaynak

İstemci API'si, 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, etkin hizmet çalışanını doğrudan serviceWorker arayüzü üzerinden 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ı, müşterileriyle geri iletişim kurmak için Clients.matchAll() ve Clients.get() gibi yöntemleri yürüterek bir dizi WindowClient nesnesi elde eder. Ardından, aşağıdakilerden birini yapabilirsiniz: 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 hizmet çalışanının bir dizi istemciyle iletişim kurduğunu gösteren şema.

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 büyük tarayıcılar tarafından desteklenir ancak tüm yöntemleri kullanılamayabilir. Bu nedenle, API'yi sitenize uygulamadan önce tarayıcı desteğini kontrol edin.

Mesaj Kanalı

Tarayıcı desteği

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

Kaynak

Mesaj Kanalı, iki yönlü bir iletişim kanalı oluşturmak için bir bağlantı noktasını tanımlamayı ve bir bağlamdan diğerine iletmeyi gerektirir.

Sayfa, kanalı başlatmak için bir MessageChannel nesnesi oluşturur ve kayıtlı hizmet işçisine bağlantı göndermek için bu nesneyi kullanır. Sayfa, diğer bağlamdan mesaj almak için üzerinde bir onmessage dinleyici de uygular:

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 sayfanın bir bağlantı noktasını hizmet işçisine iletmesini gösteren şema.

Hizmet çalışanı bağlantı noktasını alır, bağlantı noktasına ait 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 Planda Getirme

Bu kılavuzda, yapılacak işlemi açıklayan bir dize mesajı veya bir bağlamdan diğerine önbelleğe alınacak URL'lerin listesi gibi nispeten basit durumlarda iki yönlü iletişim tekniklerini uygulamanın yollarını inceledik. Bu bölümde, belirli senaryoları (bağlantı olmaması ve uzun indirmeler) ele almak için iki API'yi inceleyeceğiz.

Arka Plan Senkronizasyonu

Tarayıcı desteği

  • Chrome: 49.
  • Edge: 79.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Kaynak

Sohbet uygulamaları, kötü bağlantı nedeniyle mesajların hiçbir zaman kaybolmamasını sağlayabilir. Arka Plan Senkronizasyonu API'si, kullanıcının bağlantısı sabit olduğunda yeniden denenecek işlemleri ertelemenize olanak tanır. Bu, kullanıcının göndermek istediği ş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ı olup olmadığını belirten bir promise döndürmelidir. İşlev başarılı olursa senkronizasyon tamamlanır. Bu işlem başarısız olursa yeniden denemek için başka bir senkronizasyon planlanır. Senkronizasyonların yeniden denenmesi de bağlantıyı bekler ve üstel geri yükleme kullanır.

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

Google Arama, kötü bağlantı nedeniyle başarısız olan sorguları korumak ve kullanıcı internete bağlıyken bunları daha sonra yeniden denemek için Arka Plan Senkronizasyonu'nu kullanır. İşlem gerçekleştirildikten sonra sonuç, web push bildirimi aracılığıyla kullanıcıya iletilir:

İki yönlü iletişim kurmak için bir sayfanın bir bağlantı noktasını hizmet işçisine iletmesini gösteren şema.

Arka planda getirme

Tarayıcı desteği

  • Chrome: 74.
  • Edge: 79.
  • Firefox: Desteklenmez.
  • Safari: Desteklenmez.

Kaynak

İleti gönderme veya önbelleğe alınacak URL'lerin listesi gibi nispeten kısa işlemler için şimdiye kadar keşfedilen seçenekler iyi bir seçimdir. 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 atılmış olur.

Arka Planda Getirme API'si, film, podcast veya oyun seviyesi indirme gibi uzun süren bir görevi hizmet işleyiciye aktarmanıza olanak tanır.

Sayfadan hizmet çalışanıyla iletişim kurmak 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 indirme işleminin ilerleme durumunu takip etmek için progress etkinliğini dinlemesine 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 sayfanın bir bağlantı noktasını hizmet işçisine iletmesini gösteren şema.
Kullanıcı arayüzü, indirme işleminin ilerleme durumunu (solda) gösterecek şekilde güncellenir. Hizmet çalışanları sayesinde, tüm sekmeler kapatıldığında (sağda) işlem çalışmaya devam edebilir.

Sonraki adımlar

Bu kılavuzda, sayfa ile hizmet işçileri arasındaki en genel iletişim şeklini (iki yönlü iletişim) inceledik.

Çoğu zaman, bir kullanıcının yanıt almadan diğer kullanıcıyla iletişim kurması için yalnızca bir bağlama ihtiyacı olabilir. Sayfalarınızda servis işleyiciye ve servis işleyiciden tek yönlü teknikleri nasıl uygulayacağınızla ilgili yol gösterici bilgiler, 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ı çağırma (ör. ön getirme senaryolarında).
  • Güncellemeleri yayınlama: Önemli güncellemeler (ör. web uygulamasının yeni bir sürümü kullanıma sunuldu) hakkında bilgi vermek için sayfayı hizmet çalışanından çağırma.