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 ve Service Worker arasında iki yönlü bir iletişim kanalı oluşturması gerekebilir.

Örneğin, bir podcast PWA'da, kullanıcının çevrimdışı tüketim için bölümleri indirmesini sağlayan bir özellik oluşturulabilir ve hizmet çalışanı sayfayı ilerleme durumu hakkında düzenli olarak bilgilendirebilir. 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 durumları keşfederek Pencere ve hizmet çalışanı bağlamı arasında iki yönlü iletişim kurmanın farklı yollarını keşfedeceğiz.

Bir hizmet çalışanı ile mesajlaşan sayfayı gösteren şema.

Workbox'ı kullanma

workbox-window, Workbox kitaplığının pencere bağlamında çalışması amaçlanan bir dizi modüldü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 hizmet çalışanına, sürümünü alması 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);

Service Worker diğer tarafta 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ıkta, bir sonraki bölümde inceleyeceğimiz bir tarayıcı API'si kullanılır: Message Channel, ancak bu API'nin sahip olduğu geniş tarayıcı desteğinden yararlanırken birçok uygulama ayrıntısını soyutlayarak kullanımı 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 ve Service Worker'lar arasında "iki yönlü" iletişimi uygulamak için kullanılabilecek alt düzey API'ler mevcuttur. Bunların bazı benzerlikleri ve farklılıkları vardır:

Benzerlikler:

  • Her durumda, iletişim postMessage() arayüzü aracılığıyla bir uçta başlar ve message işleyicisi uygulanarak diğer uçta 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ştirme sürecini basitleştirebilir.

Farklılıklar:

  • İletişimin diğer tarafını tanımlamanın farklı yolları vardır: Bazıları diğer bağlama dair açık bir referans kullanırken diğerleri her iki tarafında somutlaştırılan bir proxy nesnesi aracılığıyla dolaylı yoldan iletişim kurabilir.
  • Tarayıcı desteği, ürünler arasında farklılık gösterir.
Sayfa ile Service Worker arasındaki iki yönlü iletişimi ve kullanılabilir tarayıcı API'lerini gösteren şema.

Yayın Kanalı API'sı

Tarayıcı Desteği

  • 54
  • 79
  • 38
  • 15,4

Kaynak

Broadcast Channel API, BroadcastChannel nesneleri aracılığıyla göz atma 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, herhangi bir dinleme bağlamına 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örüldüğü gibi, belirli bir bağlama yönelik açık bir referans olmadığı için önce Service Worker'a veya belirli bir istemciye referans alınmasına gerek yoktur.

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

Dezavantajı, bu yazının hazırlandığı anda API'nin Chrome, Firefox ve Edge'den desteklenmesi ancak Safari gibi diğer tarayıcıların henüz desteklememektedir.

İstemci API'sı

Tarayıcı Desteği

  • 40
  • 17
  • 44
  • 11.1

Kaynak

Client API, Service Worker'ın kontrol ettiği etkin sekmeleri temsil eden tüm WindowClient nesnelerine referans almanızı sağlar.

Sayfa tek bir hizmet çalışanı tarafından kontrol edildiğinden doğrudan serviceWorker arayüzü üzerinden etkin hizmet çalışanını dinler ve 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ı da bir onmessage işleyicisi uygulayarak mesajları dinler:

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

Service Worker, istemcilerinden biriyle tekrar iletişim kurmak için Clients.matchAll() ve Clients.get() gibi yöntemleri çalıştırarak bir dizi WindowClient nesnesi edinir. Bu durumda aşağıdakilerden herhangi birini postMessage() yapabilir:

//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 müşteriyle iletişim kurduğunu gösteren şema.

Client API, bir Service Worker'ın etkin sekmeleriyle nispeten basit bir şekilde kolayca iletişim kurmak için iyi bir seçenektir. API, başlıca tüm tarayıcılar tarafından desteklenir ancak API'nin tüm yöntemleri mevcut olmayabilir. Bu nedenle, API'yi sitenizde uygulamadan önce tarayıcı desteğini kontrol ettiğinizden emin olun.

Mesaj Kanalı

Tarayıcı Desteği

  • 2
  • 12
  • 41
  • 5

Kaynak

Mesaj Kanalı, iki yönlü bir iletişim kanalı kurmak için bir bağlantı noktasının tanımlanıp bir bağlamdan diğerine geçirilmesini gerektirir.

Sayfa, kanalı ilk kullanıma hazırlamak için MessageChannel nesnesini örneklendirir ve bunu kayıtlı hizmet çalışanına bir bağlantı noktası göndermek için kullanır. Sayfa, diğer bağlamdan mesajları almak için ona bir onmessage işleyicisi 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 Service Worker'a bağlantı noktası geçiren bir sayfayı gösteren şema.

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 bunu 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 başlıca tüm tarayıcılar tarafından desteklenmektedir.

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

Bu kılavuzda, gerçekleştirilecek işlemi açıklayan bir dize mesajı iletme veya bir bağlamdan diğerine önbelleğe alınacak URL listesi gibi görece basit durumlar için iki yönlü iletişim tekniklerini uygulamanın yollarını araştırdık. Bu bölümde, belirli senaryolara yönelik iki API'yi keşfedeceğiz: bağlantı eksikliği ve uzun indirme işlemleri.

Arka Planda Senkronizasyon

Tarayıcı Desteği

  • 49
  • 79
  • x
  • x

Kaynak

Bir sohbet uygulaması, kötü bağlantı nedeniyle mesajların hiçbir zaman kaybolmadığından emin olmak isteyebilir. Arka Plan Senkronizasyonu API'si kullanıcı kararlı bağlantıya sahip olduğunda işlemlerin yeniden denenmesini ertelemenize olanak tanır. Bu, kullanıcının göndermek istediği şeyin gerçekten gönderildiğinden emin olmak için yararlıdır.

postMessage() arayüzü yerine sayfa 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 vaat döndürmelidir. Bu işlev işlev görürse senkronizasyon tamamlanır. İşlem başarısız olursa yeniden denemek üzere başka bir senkronizasyon planlanır. Yeniden deneme senkronizasyonları da bağlantıyı bekler ve eksponansiyel geri yükleme uygular.

İşlem gerçekleştirildikten sonra hizmet çalışanı, daha önce ele alınan 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ıdan kaynaklanan başarısız sorguları devam ettirmek için Arka Plan Senkronizasyonu'nu kullanır ve bu sorguları daha sonra kullanıcı internete bağlandığında yeniden dener. İşlem gerçekleştirildikten sonra, sonucu kullanıcıya bir web push bildirimi aracılığıyla iletir.

İki yönlü iletişim kurmak için Service Worker'a bağlantı noktası geçiren bir sayfayı gösteren şema.

Arka Planda Getirme

Tarayıcı Desteği

  • 74
  • 79
  • x
  • x

Kaynak

İleti gönderme veya önbelleğe alınacak URL listesi gibi nispeten kısa iş parçaları için, şimdiye kadar açıklanan seçenekler iyi bir seçimdir. Görev çok uzun sürerse tarayıcı hizmet çalışanını öldürür. Aksi takdirde, kullanıcının gizliliği ve pili için bir risk söz konusudur.

Arka Planda Getirme API'si; film, podcast veya bir oyunun seviyelerini indirmek gibi uzun bir görevi Service Worker'a aktarmanıza olanak tanır.

Hizmet çalışanıyla sayfadan 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, indirme işleminin ilerlemesini izlemek için sayfanın progress etkinliğini dinlemesini sağlar:

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 Service Worker'a bağlantı noktası geçiren bir sayfayı gösteren şema.
Kullanıcı arayüzü, indirme işleminin ilerlemesini gösterecek şekilde güncellenir (solda). Service Worker'lar sayesinde işlem, tüm sekmeler kapatıldığında (sağda) çalışmaya devam edebilir.

Sonraki adımlar

Bu kılavuzda, sayfa ve hizmet çalışanları arasındaki en genel iletişim örneğini (iki yönlü iletişim) inceledik.

Çoğu zaman kullanıcı bir yanıt almadan diğeriyle iletişim kurmak için yalnızca bir içeriğe ihtiyaç duyabilir. Kullanım alanları ve üretim örnekleriyle birlikte, hizmet çalışanından gelen ve hizmet çalışanına giden tek yönlü tekniklerin sayfalarınıza nasıl uygulanacağına ilişkin yol gösterici bilgiler için aşağıdaki rehberlere göz atın:

  • Zorunlu önbelleğe alma kılavuzu: Kaynakları önceden önbelleğe almak için sayfadan bir hizmet çalışanının çağrılması (ör. önceden getirme senaryolarında).
  • Güncellemeleri yayınlama: Önemli güncellemeler hakkında bilgi vermek için hizmet çalışanının sayfaya çağrı yapması (ör. web uygulamasının yeni bir sürümü kullanılabilir).