Bulletin ekibinin, bir PWA geliştirirken hizmet çalışanları hakkında öğrendikleri.
Bu, Google Bulletin ekibinin dışa dönük bir PWA geliştirirken edindiği derslerle ilgili bir blog yayını serisinin ilk bölümüdür. Bu yayınlarda, karşılaştığımız bazı zorlukları, bu zorlukları aşmak için uyguladığımız yaklaşımları ve sık yapılan hatalardan kaçınmaya yönelik genel tavsiyeleri paylaşacağız. Bu, PWA'lara dair eksiksiz bir genel bakış değildir. Amacımız, ekibimizin tecrübelerinden öğrendiklerimizi paylaşmaktır.
Bu ilk yayında, önce biraz arka plan bilgisi vereceğiz ve ardından hizmet işçileri hakkında öğrendiklerimizi ayrıntılı olarak ele alacağız.
Arka plan
Bülten, 2017'nin ortalarından 2019'un ortalarına kadar aktif olarak geliştiriliyordu.
PWA oluşturmayı tercih etmemizin nedeni
Geliştirme sürecine değinmeden önce, bu proje için neden bir PWA oluşturmanın cazip bir seçenek olduğunu inceleyelim:
- Hızlı iterasyon yapma imkanı. Bulletin birden fazla pazarda pilot olarak kullanılacağından özellikle değerli.
- Tek kod tabanı. Kullanıcılarımız Android ve iOS arasında aşağı yukarı eşit bir şekilde dağılmıştı. PWA sayesinde her iki platformda da çalışacak tek bir web uygulaması oluşturabildik. Bu, ekibin hızını ve etkisini artırdı.
- Kullanıcı davranışından bağımsız olarak hızlı bir şekilde güncellenir. PWA'lar otomatik olarak güncellenip güncel olmayan istemci sayısını azaltır. Müşteriler için çok kısa bir taşıma süresiyle, arka uçta önemli değişiklikleri kullanıma sunabildik.
- İlk ve üçüncü taraf uygulamalarıyla kolayca entegre edilebilir. Bu tür entegrasyonlar uygulama için bir gereklilikti. PWA ile genellikle yalnızca bir URL açılıyordu.
- Uygulama yükleme zorluğu ortadan kaldırıldı.
Çerçevemiz
Bulletin için Polymer'i kullandık ancak iyi desteklenen modern bir çerçeve de kullanılabilir.
Hizmet çalışanları hakkında öğrendiklerimiz
Hizmet çalışanı olmadan PWA'nız olamaz. Servis çalışanları, gelişmiş önbelleğe alma stratejileri, çevrimdışı özellikler, arka plan senkronizasyonu gibi birçok avantaj sunar. Servis çalışanları bazı karmaşıklıklar da getirse de sağladıkları avantajların, ek karmaşıklıktan daha ağır bastığını tespit ettik.
Mümkünse oluşturun
Hizmet çalışanı komut dosyasını manuel olarak yazmamaya çalışın. Service Worker'ları el ile yazmak, önbelleğe alınan kaynakların manuel olarak yönetilmesini ve Workbox gibi çoğu Service Worker kitaplığında ortak olan mantığın yeniden yazılmasını gerektirir.
Bununla birlikte, şirket içi teknoloji grubumuzun yapısı nedeniyle hizmet işleyicimizi oluşturmak ve yönetmek için bir kitaplık kullanamadık. Aşağıdaki bilgilerde bu durum bazen yansıtılabilir. Daha fazla bilgi edinmek için Oluşturulmayan hizmet çalışanları için sorunlar bölümüne gidin.
Tüm kitaplıklar hizmet çalışanlarıyla uyumlu değildir
Bazı JS kitaplıkları, bir hizmet çalışanı tarafından çalıştırıldığında beklendiği gibi çalışmayan varsayımlarda bulunur. Örneğin, window
veya document
'ün kullanılabilir olduğu varsayılarak veya hizmet çalışanlarının kullanamadığı bir API'nin (XMLHttpRequest
, yerel depolama alanı vb.) kullanılması. Uygulamanız için ihtiyaç duyduğunuz kritik kitaplıkların hizmet çalışanlarıyla uyumlu olduğundan emin olun. Bu PWA'da kimlik doğrulama için gapi.js kullanmak istedik ancak hizmet çalışanlarını desteklemediğinden kullanamadık. Kitaplık yazarları, hizmet çalışanı kullanım alanlarını desteklemek için mümkün olduğunda JavaScript bağlamıyla ilgili gereksiz varsayımları da azaltmalı veya kaldırmalıdır (ör. hizmet çalışanıyla uyumlu olmayan API'lerden ve global durumdan kaçınarak).
İlk kullanıma hazırlama sırasında IndexedDB'e erişmekten kaçının
Hizmet çalışanı komut dosyanızı başlatırken IndexedDB'i okumayın. Aksi takdirde, istenmeyen bir durumla karşılaşabilirsiniz:
- Kullanıcının IndexedDB (IDB) sürümü N olan web uygulaması var
- Yeni web uygulaması, N+1 IDB sürümüyle birlikte gönderilir
- Kullanıcı PWA'yı ziyaret eder ve bu da yeni hizmet çalışanının indirilmesini tetikler.
- Yeni hizmet çalışanı,
install
etkinlik işleyicisini kaydetmeden önce IDB'den veri okur ve N'den N+1'e geçmek için bir IDB yükseltme döngüsü tetikler - Kullanıcının N sürümüne sahip eski bir istemcisi olduğundan, etkin bağlantılar veritabanının eski sürümünde hâlâ açık olduğu için hizmet çalışanı yükseltme işlemi askıya alınır.
- Hizmet çalışanı takılıyor ve hiçbir zaman yüklenmiyor
Bizim durumumuzda, servis çalışanı yüklenirken önbellek geçersiz kılındı. Bu nedenle, servis çalışanı hiç yüklenmezse kullanıcılar güncellenmiş uygulamayı hiç almaz.
Dayanıklı hale getirin
Servis çalışanı komut dosyaları arka planda çalışsa da G/Ç işlemlerinin (ağ, IDB vb.) ortasındayken bile herhangi bir zamanda sonlandırılabilir. Uzun süreli işlemler her noktada devam ettirilebilir.
Büyük dosyaları sunucuya yükleyip IDB'ye kaydeden bir senkronizasyon işlemi durumunda, kesintiye uğrayan kısmi yüklemeler için çözümümüz, dahili yükleme kitaplığımızın devam ettirilebilir sisteminden yararlanmak, devam ettirilebilir yükleme URL'sini yüklemeden önce IDB'ye kaydetmek ve yükleme ilk kez tamamlanmamışsa yüklemeyi devam ettirmek için bu URL'yi kullanmaktı. Ayrıca uzun süren herhangi bir G/Ç işleminden önce durum, her bir kayıt için işlemin hangi aşamasında olduğumuzu belirtmek üzere IDB'ye kaydediliyor.
Global duruma bağlı olmayın
Hizmet çalışanları farklı bir bağlamda bulunduğundan, varlığını bekleyebileceğiniz birçok simge mevcut değildir. Kodumuzun büyük bir kısmı hem window
bağlamında hem de Service Worker bağlamında (ör. günlük kaydı, işaretler, senkronizasyon) çalıştırılıyordu. Kod'un, yerel depolama veya çerezler gibi kullandığı hizmetler konusunda savunma amaçlı olması gerekir. Global nesneye tüm bağlamlarda çalışacak şekilde referans vermek için globalThis
kullanabilirsiniz. Ayrıca, genel değişkenlerde depolanan verileri ölçülü bir şekilde kullanın, çünkü komut dosyasının ne zaman sonlandırılacağı ve durumun ne zaman çıkarılacağı konusunda garanti verilmez.
Yerel geliştirme
Hizmet işçilerinin önemli bir bileşeni, kaynakları yerel olarak önbelleğe almaktır. Ancak geliştirme sırasında bu durum, özellikle güncellemeler tembelce yapıldığında, istediğinizin tam zıttı olur. Sunucu çalışanını, ilgili sorunlarda hata ayıklama yapabilmek veya arka plan senkronizasyonu ya da bildirimler gibi diğer API'lerle çalışmak için yine de yüklemeniz gerekir. Chrome'da bunu Chrome Geliştirici Araçları üzerinden gerçekleştirebilirsiniz. Bunun için Ağ panelinde Önbelleği devre dışı bırak onay kutusunu etkinleştirin (Uygulama paneli > Hizmet çalışanları bölmesi) ve bellek önbelleğini de devre dışı bırakmak için Ağ panelinde Ağ için atla onay kutusunu etkinleştirin. Daha fazla tarayıcıyı kapsayabilmek için geliştirici derlemelerinde varsayılan olarak etkinleştirilen, hizmet işleyicimize önbelleğe almayı devre dışı bırakan bir işaret ekleyerek farklı bir çözüme yöneldik. Bu sayede geliştiriciler, herhangi bir önbelleğe alma sorunu yaşamadan her zaman en son değişiklikleri alır. Tarayıcının öğeleri önbelleğe almasını önlemek için Cache-Control: no-cache
başlığını da eklemeniz önemlidir.
Deniz Feneri
Lighthouse, PWA'lar için yararlı olan çeşitli hata ayıklama araçları sağlar. Bir siteyi tarar ve PWA'ları, performansı, erişilebilirliği, SEO'yu ve diğer en iyi uygulamaları kapsayan raporlar oluşturur. PWA olma ölçütlerinden birini ihlal ederseniz sizi uyarması için Lighthouse'u sürekli entegrasyonda çalıştırmanızı öneririz. Bu durum bizim de başımıza bir kez geldi. Hizmet çalışanı yüklenmiyordu ve bunu üretime göndermeden önce fark etmedik. Lighthouse'ı CI'mizin bir parçası haline getirmiş olsaydık bu sorunu önleyebilirdik.
Sürekli teslimi benimseyin
Hizmet çalışanları otomatik olarak güncellenebildiğinden kullanıcılar yükseltmeleri sınırlayamaz. Bu, kullanımdaki güncel olmayan istemci sayısını önemli ölçüde azaltır. Kullanıcı uygulamamızı açtığında hizmet çalışanı, yeni istemciyi yavaşça indirirken eski istemciyi sunuyordu. Yeni istemci indirildikten sonra, yeni özelliklere erişmek için kullanıcıdan sayfayı yenilemesi istenir. Kullanıcı bu isteği yoksaysa bile sayfayı bir sonraki sefer yenilediğinde istemcinin yeni sürümünü alır. Sonuç olarak, kullanıcıların güncellemeleri iOS/Android uygulamaları için yapabildikleri şekilde reddetmesi oldukça zordur.
Müşteriler için çok kısa bir taşıma süresiyle, önemli arka uç değişikliklerini kullanıma sunabildik. Genellikle, önemli değişiklikler yapmadan önce kullanıcıların yeni istemcileri güncellemesi için bir ay süre tanırız. Uygulama eskiyken yayınlanacağından, kullanıcı uygulamayı uzun süre açmamışsa eski istemcilerin kullanımda kalması mümkündü. iOS'te hizmet çalışanları birkaç hafta sonra çıkarılır. Bu nedenle bu durum yaşanmaz. Android'de bu sorun, eski içerikler yayınlanmadığında veya içeriklerin süresi birkaç hafta sonra manuel olarak sona erdirildiğinde azaltılabilir. Uygulamada, eski istemcilerden kaynaklanan sorunlarla hiç karşılaşmadık. Belirli bir ekibin bu konuda ne kadar katı olmak istediği, söz konusu kullanım alanına bağlıdır. Ancak PWA'lar, iOS/Android uygulamalarına kıyasla çok daha fazla esneklik sağlar.
Hizmet çalışanında çerez değerlerini alma
Bazen bir hizmet çalışanı bağlamında çerez değerlerine erişmek gerekir. Bizim durumumuzda, birinci taraf API isteklerinin kimliğini doğrulamak için jeton oluşturmak üzere çerez değerlerine erişmemiz gerekiyordu. Service Worker'da document.cookies
gibi eşzamanlı API'ler kullanılamaz. Çerez değerlerini istemek için hizmet işleyiciden etkin (pencerelenmiş) istemcilere dilediğiniz zaman mesaj gönderebilirsiniz. Ancak hizmet işleyicinin, arka plan senkronizasyonu sırasında olduğu gibi pencerelenmiş istemci olmadan arka planda çalışması mümkündür. Bu sorunu gidermek için ön uç sunucumuzda, çerez değerini istemciye geri yansıtan bir uç nokta oluşturduk. Hizmet çalışanı, bu uç noktaya ağ isteği gönderdi ve çerez değerlerini almak için yanıtı okudu.
Cookie Store API'nin kullanıma sunulmasıyla birlikte, tarayıcı çerezlerine eşzamansız erişim sağladığı ve doğrudan hizmet çalışanı tarafından kullanılabildiği için bu geçici çözüm, bu API'yi destekleyen tarayıcılarda artık gerekli olmayacaktır.
Oluşturulmayan Service Worker'ların sorunları
Statik önbelleğe alınmış dosyalarda değişiklik olursa hizmet çalışanı komut dosyasının değiştiğinden emin olun
Yaygın bir PWA kalıbı, bir hizmet çalışanının tüm statik uygulama dosyalarını install
aşamasında yüklemesidir. Bu, istemcilerin sonraki tüm ziyaretler için doğrudan Cache Storage API önbelleğini kullanmasına olanak tanır. Servis çalışanları yalnızca tarayıcı, servis çalışanı komut dosyasının bir şekilde değiştiğini algıladığında yüklenir. Bu nedenle, önbelleğe alınmış bir dosya değiştiğinde servis çalışanı komut dosyasının da bir şekilde değiştiğinden emin olmamız gerekiyordu. Statik kaynak dosya kümesinin bir karmasını Service Worker komut dosyamıza yerleştirerek bu işlemi manuel olarak gerçekleştirdik. Böylece her sürüm, ayrı bir Service Worker JavaScript dosyası üretti. Workbox gibi hizmet çalışanı kitaplıkları bu işlemi sizin için otomatikleştirir.
Birim testi
Service Worker API'leri, global nesneye etkinlik işleyiciler ekleyerek çalışır. Örneğin:
self.addEventListener('fetch', (evt) => evt.respondWith(fetch('/foo')));
Bu, test etmek zor olabilir. Çünkü etkinlik tetikleyicisi ve etkinlik nesnesiyle alay etmeniz, respondWith()
geri çağırmasını ve ardından son olarak sonuç hakkında açıklama yapmadan önce taahhüdü beklemeniz gerekir. Bu yapıyı oluşturmanın daha kolay bir yolu, tüm uygulamayı daha kolay test edilebilen başka bir dosyaya devretmektir.
import fetchHandler from './fetch_handler.js';
self.addEventListener('fetch', (evt) => evt.respondWith(fetchHandler(evt)));
Bir hizmet çalışanı komut dosyasını birim test etmenin zorlukları nedeniyle, temel hizmet çalışanı komut dosyasını mümkün olduğunca basit tutarak uygulamanın büyük bir kısmını diğer modüllere ayırdık. Bu dosyalar yalnızca standart JS modülleri olduğundan, standart test kitaplıklarıyla daha kolay birim test edilebilirler.
2. ve 3. bölümler için bizi takip edin.
Bu serinin 2. ve 3. bölümlerinde medya yönetimi ve iOS'e özgü sorunlardan bahsedeceğiz. Google'da PWA oluşturma hakkında daha fazla bilgi edinmek için yazar profillerimizi ziyaret ederek bizimle nasıl iletişime geçeceğinizi öğrenin: