Google'da PWA oluşturma, 1. bölüm

Bulletin ekibinin, bir PWA geliştirirken hizmet çalışanları hakkında öğrendikleri.

Douglas Parker
Douglas Parker
Joel Riley
Joel Riley
Dikla Cohen
Dikla Cohen

Bu, Google Bulletin ekibinin harici 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 deneyimlerinden edindiğimiz bilgileri 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 yaklaşık olarak eşit olarak bölünmüştür. 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üncellenebilir. Bu sayede, kullanımdaki güncel olmayan istemci sayısı azaltılabilir. 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 şarttı. PWA'larda bu genellikle bir URL'yi açmak anlamına geliyordu.
  • Uygulama yüklemeyle ilgili zorlukları ortadan kaldırdık.

Ç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. Hizmet çalışanlarını manuel olarak yazmak, önbelleğe alınmış kaynakları manuel olarak yönetmeyi ve Workbox gibi çoğu hizmet çalışanı kitaplığında ortak olan mantığı yeniden yazmayı 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 için Oluşturulmayan hizmet çalışanlarıyla ilgili tuzaklar başlıklı makaleyi inceleyin.

Tüm kitaplıklar hizmet çalışanı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ımlar yapar. Örneğin, window veya document'ün kullanılabildiği 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ışanıyla uyumlu olduğundan emin olun. Bu belirli PWA için kimlik doğrulama amacıyla gapi.js kullanmak istedik ancak hizmet çalışanlarını desteklemediği için bunu yapamadı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 küresel 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:

  1. Kullanıcının N sürümüne sahip IndexedDB (IDB) web uygulaması var
  2. Yeni web uygulaması, N+1 IDB sürümüyle birlikte gönderilir
  3. Kullanıcı PWA'yı ziyaret eder ve bu da yeni hizmet çalışanının indirilmesini tetikler.
  4. 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
  5. 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.
  6. Hizmet çalışanı takılıyor ve hiçbir zaman yüklenmiyor

Bizim durumumuzda, önbellek servis çalışanı yüklenirken geçersiz kılındı. Bu nedenle, servis çalışanı hiç yüklenmezse kullanıcılar güncellenmiş uygulamayı hiç almaz.

Esnek bir yapı oluşturun

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üren tüm işlemler herhangi bir noktada devam ettirilebilir olmalıdır.

Büyük dosyaları sunucuya yükleyip IDB'ye kaydeden bir senkronizasyon işlemi söz konusu olduğunda, kesintiye uğrayan kısmi yüklemeler için çözümümüz, dahili yükleme kitaplığımızın devam ettirilebilir sisteminden yararlanmaktı. Bu sistemde, devam ettirilebilir yükleme URL'si yükleme işleminden önce IDB'ye kaydedilir ve yükleme ilk kez tamamlanmazsa bu URL kullanılarak devam ettirilir. Ayrıca, uzun süren herhangi bir G/Ç işleminden önce, her kayıt için sürecin neresinde olduğumuzu belirtmek üzere durum IDB'ye kaydediliyordu.

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. Kodlarımızın çoğu hem window bağlamında hem de hizmet çalışanı bağlamında (ör. günlük kaydı, işaretler, senkronizasyon) çalıştırıldı. Kodun, kullandığı hizmetler (ör. yerel depolama alanı veya çerezler) konusunda savunmacı olması gerekir. Global nesneye tüm bağlamlarda çalışacak şekilde referans vermek için globalThis kullanabilirsiniz. Ayrıca, komut dosyasının ne zaman sonlandırılacağı ve durumun ne zaman kaldırılacağı konusunda garanti olmadığından, global değişkenlerde depolanan verileri de dikkatli bir şekilde kullanın.

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 işleyicisini, 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 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 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şleyicimizde önbelleğe almayı devre dışı bırakma işaretçisi 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 kullanıcıdan yeni özelliklere erişmek için 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. Hizmet çalışanlarında document.cookies gibi senkron 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 bir 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 asenkron 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 hizmet çalışanlarıyla ilgili 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. Bunu, statik kaynak dosya grubunun karmasını hizmet çalışanı komut dosyamıza yerleştirerek manuel olarak yaptık. Böylece her sürümde farklı bir hizmet çalışanı JavaScript dosyası oluşturuldu. Workbox gibi hizmet çalışanı kitaplıkları bu işlemi sizin için otomatikleştirir.

Birim testi

Hizmet çalışanı API'leri, genel nesneye etkinlik dinleyicileri ekleyerek çalışır. Örneğin:

self.addEventListener('fetch', (evt) => evt.respondWith(fetch('/foo')));

Son olarak sonuç için iddiada bulunmadan önce etkinlik tetikleyicisini, etkinlik nesnesini taklit etmeniz, respondWith() geri çağırma işlevini beklemeniz ve ardından promise'i beklemeniz gerektiğinden bu testin yapılması zor olabilir. 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 testi yapılabiliyordu.

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: