Progresif Web Uygulamanızı kademeli olarak iyileştirin

Modern tarayıcılar için geliştirme ve 2003 yılında olduğu gibi sürekli olarak gelişiyor

Mart 2003'te Nick Finck ve Steve Champeon web tasarımı dünyasını hayrete düşürdü konseptiyle progresif geliştirme önce temel web sayfası içeriğinin yüklenmesini vurgulayan bir web tasarımı stratejisidir. ve ardından gittikçe daha fazla ayrıntı içeren ve teknik olarak titizlikle hazırlanmış sunum katmanları ve 2003'te, progresif geliştirmeler o dönemde modern CSS özellikleri, göze çarpmayan JavaScript ve hatta yalnızca Ölçeklenebilir Vektör Grafikleri. 2020 ve sonrasında ileriye dönük geliştirmeler, modern tarayıcı özellikleri hakkında daha fazla bilgi edinin.

Progresif geliştirmelerle geleceğin kapsayıcı web tasarımı. Finck ve Champeon'un orijinal sunusunun başlık slaytı.
Slayt: Progresif Geliştirme ile Geleceğin Kapsayıcı Web Tasarımı. (Kaynak)

Modern JavaScript

JavaScript'ten bahsetmişken, en son temel ES 2015 JavaScript'i için tarayıcı desteği durumu harikaydı. Yeni standart; vaatler, modüller, sınıflar, şablon metinleri, ok işlevleri, let ve const, varsayılan parametreler, oluşturucular, yapı bozucu atama, dinlenme ve yayılma, Map/Set, WeakMap/WeakSet ve çok daha fazlası. Tümü desteklenir.

Önde gelen tüm tarayıcılarda desteği gösteren, ES6 özellikleri için CanIUse destek tablosu.
ECMAScript 2015 (ES6) tarayıcı destek tablosu. (Kaynak)

Eş zamansız işlevler, bir ES 2017 özelliği ve benim en sevdiklerimden biri. kullanılabilir kullandığınızdan emin olun. async ve await anahtar kelimeleri eşzamansız, vaade dayalı davranışları etkinleştirir. Böylece, taahhüt zincirlerini açıkça yapılandırma gereksinimini ortadan kaldırır.

Önde gelen tüm tarayıcılarda desteği gösteren eş zamansız işlevler için CanIUse destek tablosu.
Eş zamansız işlevler tarayıcı desteği tablosu. (Kaynak)

Hatta İngilizce ve İspanyolca 2020 gibi isteğe bağlı zincirleme ve nullish birleştirme gerçekten hızlı bir şekilde ulaştık. Aşağıda kod örneğini görebilirsiniz. Temel JavaScript özellikleri söz konusu olduğunda, çimenlik bundan çok daha yeşil olamazdı bugünün adı.

const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah',
  },
};
console.log(adventurer.dog?.name);
// Expected output: undefined
console.log(0 ?? 42);
// Expected output: 0
Simgeleşmiş Windows XP yeşil çim arka plan resmi.
Temel JavaScript özellikleri söz konusu olduğunda çimler yeşildir. (Microsoft ürün ekran görüntüsü, izin alın.)

Örnek uygulama: Fugu Greetings

Bu makale için basit bir PWA ile çalışıyorum. Fugu Tebrik Kartları (GitHub). Bu uygulamanın adı, Web'in her şeyini ortaya çıkarmayı amaçlayan Fugu Projesi'ne 🐡 daha da güçlü bir araç haline geldi. Proje hakkında daha fazla bilgiyi açılış sayfası.

Fugu Greetings, sanal tebrik kartları oluşturup göndermenize olanak tanıyan bir çizim uygulamasıdır. bir e-posta alırsınız. Örneklendirdiği PWA'nın temel kavramları. İnsanların güvenilir ve tamamen çevrimdışıdır. Böylece çevrimdışıyken bile bir ağınız varsa bu ağı kullanmaya devam edebilirsiniz. Ayrıca Yüklenebilir cihazın ana ekranına bağlanır ve işletim sistemiyle sorunsuz bir şekilde entegre olur bağımsız bir uygulama olarak görür.

Fugu, PWA topluluğunun logosuna benzeyen bir çizimle PWA'yı selamlıyor.
Fugu Greetings örnek uygulaması.

Progresif geliştirme

Bunu yaptıktan sonra, progresif geliştirme hakkında konuşabiliriz. MDN Web Dokümanlar Sözlüğü tanımlar şu şekilde düşünebilirsiniz:

Progresif geliştirme, geliştirme ve uygulama için temel çizgisi sağlayan bir tasarım felsefesidir. olabildiğince çok sayıda kullanıcıya sunmak için temel içerik ve işlevsellik mümkün olan en iyi deneyimi sunarak yalnızca tarayıcılarla çalışır.

Özellik algılama tarayıcıların daha modern işlevleri işleyip işleyemediğini belirlemek için kullanılır. çoklu dolgular genellikle JavaScript'teki eksik özellikleri eklemek için kullanılır.

[…]

Progresif geliştirme, web geliştiricilerinin belirli noktalara odaklanmalarını sağlayan mümkün olan en iyi web sitelerini geliştirmeye odaklanırken birden fazla bilinmeyen kullanıcı aracısına uygular. Değerli azalma alakalıdır ancak aynı şey değildir ve genellikle ters yönde ilerler geliştirmeye odaklanacağız. Gerçekte, her iki yaklaşım da geçerlidir ve genellikle birbirini tamamlayabilir.

MDN katkıda bulunanlar

Her tebrik kartını sıfırdan başlatmak gerçekten külfetli olabilir. Öyleyse neden kullanıcıların resimleri içe aktarıp oradan başlamalarını sağlayan bir özelliğe sahip değil misiniz? Geleneksel bir yaklaşımda ise <input type=file> öğesi seçin. İlk olarak öğeyi oluşturur, type öğesini 'file' olarak ayarlar ve MIME türlerini accept özelliğine eklersiniz, sonra programlı bir şekilde ve değişiklikleri dinleyin. Bir resim seçtiğinizde bu resim doğrudan zemine aktarılır.

const importImage = async () => {
  return new Promise((resolve) => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'image/*';
    input.addEventListener('change', () => {
      resolve(input.files[0]);
    });
    input.click();
  });
};

Bir içe aktarma özelliği olduğunda, muhtemelen bir dışa aktarma özelliği bulunmalıdır Böylece kullanıcılar tebrik kartlarını yerel olarak kaydedebilir. Geleneksel dosya kaydetme yöntemi, bir bağlayıcı bağlantısı oluşturmaktır. bir download ile özelliğine ve href olarak bir blob URL'sine sahip olmalıdır. Ayrıca, programlı bir şekilde indirme işlemini tetiklemesi için ve bellek sızıntılarını önlemek için blob nesne URL'sini iptal etmeyi unutmayın.

const exportImage = async (blob) => {
  const a = document.createElement('a');
  a.download = 'fugu-greeting.png';
  a.href = URL.createObjectURL(blob);
  a.addEventListener('click', (e) => {
    setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
  });
  a.click();
};

Lütfen bir dakika bekleyin. Zihinsel olarak, videoyu "indirmediniz" bir tebrik kartı "kaydedildi" somut olarak ortaya koyar. Size "kaydetme" özelliğini göstermek yerine, dosyayı nereye yerleştireceğinizi, Tarayıcı, tebrik kartını kullanıcı etkileşimi olmadan doğrudan indirmiştir. ve dosyayı doğrudan İndirilenler klasörünüze koydu. Bu hiç iyi değil.

Daha iyi bir yol olsaydı ne olurdu? Yerel bir dosyayı açıp düzenledikten sonra değişiklikleri kaydedebilseydiniz nasıl olurdu? veya ilk açtığınız orijinal dosyaya geri mi dönmek istiyorsunuz? Bu zaten varmış. File System Access API dosya açıp oluşturmanıza ve yapabilir, düzenleyebilir ve kaydedebilirsiniz .

Peki bir API'yi özellik algılamayla nasıl algılarım? File System Access API yeni bir window.chooseFileSystemEntries() yöntemini sunuyor. Bu nedenle, bu yöntemin kullanılabilir olup olmadığına bağlı olarak koşullu olarak farklı içe ve dışa aktarma modülleri yüklemem gerekiyor. Bunun nasıl yapılacağını aşağıda görebilirsiniz.

const loadImportAndExport = () => {
  if ('chooseFileSystemEntries' in window) {
    Promise.all([
      import('./import_image.mjs'),
      import('./export_image.mjs'),
    ]);
  } else {
    Promise.all([
      import('./import_image_legacy.mjs'),
      import('./export_image_legacy.mjs'),
    ]);
  }
};

Ancak File System Access API ayrıntılarına geçmeden önce buradaki progresif geliştirme modelini hızlıca vurgulamak istiyorum. Şu anda File System Access API'yi desteklemeyen tarayıcılarda eski komut dosyalarını yüklüyorum. Aşağıda Firefox ve Safari'nin ağ sekmelerini görebilirsiniz.

Eski dosyaların yüklenmesini gösteren Safari Web Denetleyicisi.
Safari Web Denetleyici ağ sekmesi
ziyaret edin.
'nı inceleyin.
Eski dosyaların yüklenmesini gösteren Firefox Geliştirici Araçları.
Firefox Geliştirici Araçları ağ sekmesi.

Ancak API'yi destekleyen bir tarayıcı olan Chrome'da yalnızca yeni komut dosyaları yüklenir. Bu, tüm ekran görüntülerinin bile dinamik import(); tüm modern tarayıcıların destek. Daha önce de dediğim gibi, bu günlerde çimler oldukça yeşil.

Modern dosyaların yüklenmesini gösteren Chrome Geliştirici Araçları.
Chrome Geliştirici Araçları ağ sekmesi.

File System Access API

Bu konuyu ele aldığımıza göre, artık File System Access API'ye dayalı gerçek uygulamaya bakalım. Bir resmi içe aktarmak için window.chooseFileSystemEntries() adını veriyorum ve resim dosyaları istediğimi söylediğimde bir accepts özelliği ileteceğim. Hem MIME türleri hem de dosya uzantıları desteklenir. Bu işlem sonucunda, getFile() komutunu çağırarak gerçek dosyayı alabileceğim bir dosya işleyici oluşturulur.

const importImage = async () => {
  try {
    const handle = await window.chooseFileSystemEntries({
      accepts: [
        {
          description: 'Image files',
          mimeTypes: ['image/*'],
          extensions: ['jpg', 'jpeg', 'png', 'webp', 'svg'],
        },
      ],
    });
    return handle.getFile();
  } catch (err) {
    console.error(err.name, err.message);
  }
};

Görüntüyü dışa aktarma işlemi neredeyse aynıdır, ancak bu defa chooseFileSystemEntries() yöntemine 'save-file' tür parametresini iletmem gerekiyor. Açılan sayfada "Dosya kaydetme" iletişim kutusu açılır. 'open-file' varsayılan ayar olduğundan, dosya açıkken bu işleme gerek yoktur. accepts parametresini öncekine benzer şekilde ayarladım ancak bu süre yalnızca PNG resimleriyle sınırlıdır. Yine bir dosya herkese açık kullanıcı adı alırım, ancak dosyayı almak yerine bu kez createWritable() numaralı telefonu arayarak yazılabilir bir akış oluşturuyorum. Daha sonra, tebrik kartı resmim olan blob'u dosyaya yazıyorum. Son olarak, yazılabilir akışı kapatıyorum.

Her şey başarısız olabilir: Diskte yer kalmamış olabilir, bir yazma veya okuma hatası olabilir ya da kullanıcı dosya iletişim kutusunu iptal ediyor olabilir. Bu nedenle aramaları her zaman bir try...catch ifadesinde sarmalarım.

const exportImage = async (blob) => {
  try {
    const handle = await window.chooseFileSystemEntries({
      type: 'save-file',
      accepts: [
        {
          description: 'Image file',
          extensions: ['png'],
          mimeTypes: ['image/png'],
        },
      ],
    });
    const writable = await handle.createWritable();
    await writable.write(blob);
    await writable.close();
  } catch (err) {
    console.error(err.name, err.message);
  }
};

File System Access API ile progresif geliştirmeyi kullanarak, Önceden olduğu gibi dosya açabilirim. İçe aktarılan dosya doğrudan tuvalin üzerine çizilir. Düzenlemelerimi yapıp son olarak gerçek kaydetme iletişim kutusuyla kaydedebiliyorum dosyanın adını ve depolama konumunu seçebilirim. Şimdi dosya sonsuza kadar saklanmaya hazırdır.

Fugu Greetings uygulamasında dosya açma iletişim kutusu gösteriliyor.
Dosya açma iletişim kutusu.
ziyaret edin.
'nı inceleyin.
Fugu Greetings uygulaması artık içe aktarılmış bir resim içeriyor.
İçe aktarılan resim.
ziyaret edin.
'nı inceleyin.
Değiştirilmiş resmin yer aldığı Fugu Greetings uygulaması.
Değiştirilen resim yeni bir dosyaya kaydediliyor.

Web Paylaşımı ve Web Paylaşımı Hedef API'leri

Sonsuza kadar depolamanın dışında belki de tebrik kartımı paylaşmak isterim. Bu, Web Paylaşımı API'si ve Web Share Target API sayesinde bunu yapabilirsiniz. Mobil cihazlar ve daha yakın zamanda masaüstü işletim sistemleri, yerleşik paylaşım özelliğini kazandı. mekanizmalar. Örneğin, masaüstü Safari'nin macOS'teki paylaşım sayfası, bloguma göz atabilirsiniz. Makaleyi Paylaş düğmesini tıkladığınızda makale bağlantısını arkadaşınızla paylaşabilirsiniz. örneğin macOS Mesajlar uygulaması üzerinden.

Masaüstü Safari&#39;nin macOS&#39;teki paylaşım sayfası, bir makalenin Paylaş düğmesinden tetiklendi
macOS'teki masaüstü Safari'de Web Share API.

Bunun için kullanılacak kod oldukça basittir. navigator.share() adlı kişiyi arıyorum ve bir nesnede isteğe bağlı title, text ve url iletebilir. Peki, resim eklemek istersem ne olur? Web Share API'nin 1. Düzeyi henüz bu özelliği desteklememektedir. Neyse ki Web Paylaşımı Düzeyi 2'ye dosya paylaşım özellikleri eklenmiştir.

try {
  await navigator.share({
    title: 'Check out this article:',
    text: `"${document.title}" by @tomayac:`,
    url: document.querySelector('link[rel=canonical]').href,
  });
} catch (err) {
  console.warn(err.name, err.message);
}

Bunu Fugu tebrik kartı uygulamasıyla nasıl kullanabileceğinizi size göstereyim. Öncelikle, bir blob'dan oluşan files dizisiyle bir data nesnesi hazırlamam ve daha sonra, title ve text. Ardından en iyi uygulama olarak, dönüşüm hunisinin orta kısmına yönelik navigator.canShare() yöntemini kullanıyorum adının anlamı: Paylaşmaya çalıştığım data nesnesinin tarayıcı tarafından teknik olarak paylaşılıp paylaşılamayacağını bildirir. navigator.canShare(), verilerin paylaşılabileceğini söylerse bunu yapmaya hazırım navigator.share() numaralı telefonu önceden olduğu gibi çağırın. Her şey başarısız olabileceği için yine bir try...catch bloğu kullanıyorum.

const share = async (title, text, blob) => {
  const data = {
    files: [
      new File([blob], 'fugu-greeting.png', {
        type: blob.type,
      }),
    ],
    title: title,
    text: text,
  };
  try {
    if (!(navigator.canShare(data))) {
      throw new Error("Can't share data.", data);
    }
    await navigator.share(data);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

Daha önce olduğu gibi progresif geliştirmeyi kullanıyorum. navigator nesnesinde hem 'share' hem de 'canShare' varsa yalnızca o zaman ileri gidip share.mjs dinamik import() üzerinden yükleyin. Mobil Safari gibi yalnızca iki koşuldan birini karşılayan tarayıcılarda bağlantı yüklemiyorum geri bildirim vermemiz gerekir.

const loadShare = () => {
  if ('share' in navigator && 'canShare' in navigator) {
    import('./share.mjs');
  }
};

Fugu Greetings'de, Android'de Chrome gibi desteklenen bir tarayıcıda Paylaş düğmesine dokunduğumda yerleşik paylaşım sayfası açılır. Örneğin, Gmail'i seçtiğimde e-posta oluşturma widget'ı resim eklendi.

Resmin paylaşılacağı çeşitli uygulamaların gösterildiği OS düzeyinde paylaşım sayfası.
Dosyanın paylaşılacağı uygulamayı seçme.
ziyaret edin.
'nı inceleyin.
Gmail&#39;in, resim ekli e-posta oluşturma widget&#39;ı.
Dosya, Gmail'in oluşturucusunda yeni bir e-postaya eklenir.

Contact Picker API'si

Şimdi de kişiler hakkında, yani bir cihazın adres defterinden bahsetmek istiyorum. veya kişi yöneticisi ekleyebilirsiniz. Bir tebrik kartı yazdığınızda, yazmak her zaman kolay olmayabilir. birinin adı olabilir. Örneğin, Sergey adının Kiril harfleriyle yazılmasını tercih eden bir arkadaşım var. Ben ve adlarını nasıl yazacaklarını bilmiyorlarsa. Bu, Contact Picker API'nin çözebileceği bir sorundur. Arkadaşım telefonumun kişiler uygulamasında sakladığından, Kişi Seçici API'sı aracılığıyla web'den kişilerime erişebiliyorum.

Öncelikle erişmek istediğim özelliklerin listesini belirtmem gerekiyor. Bu örnekte, yalnızca adları Ancak diğer kullanım örnekleri için telefon numaraları, e-postalar, avatarlar veya fiziksel adresler olabilir. Sonra, daha fazla öğe seçmek için bir options nesnesi yapılandırıp multiple değerini true olarak ayarlıyorum birden fazla giriştir. Son olarak, navigator.contacts.select() öğesini çağırabilirim. Bu işlem, istenen özellikleri döndürür. kullanıcı tarafından seçilen kişiler için

const getContacts = async () => {
  const properties = ['name'];
  const options = { multiple: true };
  try {
    return await navigator.contacts.select(properties, options);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

Şu ana kadar muhtemelen kalıbı öğrenmişsinizdir: Dosyayı yalnızca API gerçekten desteklendiğinde yüklüyorum.

if ('contacts' in navigator) {
  import('./contacts.mjs');
}

Fugu Karşılama'da Kişiler düğmesine dokunup en iyi iki arkadaşımı seçtiğimde 拉里"·佩奇, tüm farklı versiyonların kişi seçici sadece adlarını gösterecek şekilde e-posta adreslerini veya telefon numaraları gibi diğer bilgileri içermez. Daha sonra isimleri tebrik kartıma çizilir.

Adres defterindeki iki kişinin adını gösteren kişi seçici.
Adres defterinden kişi seçiciyi kullanarak iki ad seçme.
ziyaret edin.
'nı inceleyin.
Daha önce seçilen iki kişinin adları, tebrik kartına çizilir.
Ardından iki ad, tebrik kartına çizilir.

Eşzamansız Pano API'si

Sonraki adım kopyalama ve yapıştırma. Yazılım geliştiriciler olarak en sevdiğimiz işlemlerden biri kopyalama ve yapıştırmadır. Bir tebrik kartı yazarı olarak bazen bunu yapmak isteyebilirim. Çalıştığım tebrik kartına bir resim yapıştırıp veya tebrik kartımı kopyalayarak gönderebilirsiniz. Async Clipboard API, hem metin hem de resimleri destekler. Fugu API'sine kopyalama ve yapıştırma desteğini nasıl eklediğimi size adım adım açıklayayım. Selamlama uygulaması.

Sistemin panosuna bir şey kopyalamak için ona yazmam gerekiyor. navigator.clipboard.write() yöntemi, bir dizi pano öğesini parametresinden sonra bir değer girin. Her pano öğesi aslında değer olarak bir blob içeren bir nesnedir ve blob'un türü seçeceğiz.

const copy = async (blob) => {
  try {
    await navigator.clipboard.write([
      new ClipboardItem({
        [blob.type]: blob,
      }),
    ]);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

Yapıştırmak için, şu numarayı arayarak pano öğelerinin üzerinden geçmem gerekiyor: navigator.clipboard.read() Bunun nedeni aynı panoda birden çok pano öğesi olmasıdır. temsil eder. Her pano öğesinde, mevcut dokümanların MIME türlerini belirten bir types alanı kaynaklar. Pano öğesinin getType() yöntemini çağırarak Daha önce edindiğim MIME türü.

const paste = async () => {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      try {
        for (const type of clipboardItem.types) {
          const blob = await clipboardItem.getType(type);
          return blob;
        }
      } catch (err) {
        console.error(err.name, err.message);
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
};

Bunu şu ana kadar söylemeye gerek yok. Bunu yalnızca desteklenen tarayıcılarda yapıyorum.

if ('clipboard' in navigator && 'write' in navigator.clipboard) {
  import('./clipboard.mjs');
}

Peki, pratikte nasıl çalışıyor? macOS Önizleme uygulamasında açık bir resmim var ve panoya kopyalayın. Yapıştır'ı tıkladığımda Fugu Greetings uygulaması bana uygulamanın panodaki metin ve resimleri görmesine izin vermek isteyip istemediğimi belirler.

Fugu Greetings uygulamasında pano izin istemi gösteriliyor.
Pano izin istemi.

Son olarak da izin kabul edildikten sonra resim, uygulamaya yapıştırılır. Diğer tur da işler için geçerlidir. Bir tebrik kartını panoya kopyalayayım. Önizleme'yi açıp Dosya'yı ve ardından Panodan Yeni'yi tıkladığımda tebrik kartı yeni bir adsız resme yapıştırılır.

Adsız ve az önce yapıştırılmış bir resmin bulunduğu macOS Önizleme uygulaması.
Görsel, macOS Önizleme uygulamasına yapıştırıldı.

Badging API'si

Faydalı diğer bir API de Rozet API'sidir. Yüklenebilir bir PWA olan Fugu Greetings'in elbette uygulama simgesi Bu videolar, kullanıcıların uygulama yuvasına veya ana ekrana yerleştirebilecekleri. API'yi göstermenin eğlenceli ve kolay bir yolu, API'yi Fugu Greetings'de kullanmaktır. pek çok işlevi vardır. pointerdown etkinliği her gerçekleştiğinde kalem vuruşu sayacını artıran bir etkinlik işleyici ekledim ve güncellenmiş simge rozetini ayarlar. Kanvas temizlendiğinde sayaç sıfırlanır ve rozet kaldırılır.

let strokes = 0;

canvas.addEventListener('pointerdown', () => {
  navigator.setAppBadge(++strokes);
});

clearButton.addEventListener('click', () => {
  strokes = 0;
  navigator.setAppBadge(strokes);
});

Bu özellik progresif bir geliştirme olduğundan yükleme mantığı her zamanki gibidir.

if ('setAppBadge' in navigator) {
  import('./badge.mjs');
}

Bu örnekte, tek bir kalem çizgisi kullanarak birden yediye kadar olan sayıları çizdim. ekleyebilirsiniz. Simgenin üzerindeki rozet sayacı şu anda yedide.

Her biri tek bir kalem çizgisiyle tebrik kartına çizilen bir ile yedi arasındaki sayılar.
Yedi kalem vuruşuyla 1'den 7'ye kadar sayılar çiziliyor.
ziyaret edin.
'nı inceleyin.
Fugu Greetings uygulamasındaki 7 rakamını gösteren rozet simgesi.
Kalem vuruşları, uygulama simgesi rozeti şeklinde sayaca dönüşür.

Periodic Arka Plan Sync API'si

Her güne yeni bir şeyle başlamak ister misiniz? Fugu Greetings uygulamasının güzel bir özelliği de her sabah size ilham vermesi yeni bir arka plan resmi ekleyerek karta başlayabilirsiniz. Uygulama Periodic Background Sync API'yi kullanır. sahip olacaksınız.

İlk adım, Service Worker kaydına periyodik bir senkronizasyon etkinliği kaydetmektir. 'image-of-the-day' adlı bir senkronizasyon etiketini dinler ve minimum bir gün aralığı varsa Böylece kullanıcı 24 saatte bir yeni bir arka plan resmi alabilir.

const registerPeriodicBackgroundSync = async () => {
  const registration = await navigator.serviceWorker.ready;
  try {
    registration.periodicSync.register('image-of-the-day-sync', {
      // An interval of one day.
      minInterval: 24 * 60 * 60 * 1000,
    });
  } catch (err) {
    console.error(err.name, err.message);
  }
};

İkinci adım, hizmet çalışanında periodicsync etkinliğini dinleme işlemidir. Etkinlik etiketi 'image-of-the-day' ise, yani daha önce kaydedilen etkinlik etiketi ise günün resmi getImageOfTheDay() işlevi aracılığıyla alınır. Sonuç tüm istemcilere yayıldı ve böylece tuvallerini güncelleyip önbellek.

self.addEventListener('periodicsync', (syncEvent) => {
  if (syncEvent.tag === 'image-of-the-day-sync') {
    syncEvent.waitUntil(
      (async () => {
        const blob = await getImageOfTheDay();
        const clients = await self.clients.matchAll();
        clients.forEach((client) => {
          client.postMessage({
            image: blob,
          });
        });
      })()
    );
  }
});

Bu da tam anlamıyla ilerlemeli bir geliştirmedir. Dolayısıyla, kod yalnızca Tarayıcı, API'yi destekliyor. Bu, hem istemci kodu hem de Service Worker kodu için geçerlidir. Desteklenmeyen tarayıcılarda ikisi de yüklenmez. Dinamik bir import() yerine hizmet çalışanında bunu not edin (bu hizmet çalışanı bağlamında desteklenmez) henüz), Klasik sürümü kullanıyorum importScripts().

// In the client:
const registration = await navigator.serviceWorker.ready;
if (registration && 'periodicSync' in registration) {
  import('./periodic_background_sync.mjs');
}
// In the service worker:
if ('periodicSync' in self.registration) {
  importScripts('./image_of_the_day.mjs');
}

Fugu Selamlama'da Duvar kağıdı düğmesine basıldığında günün tebrik kartı resmi gösterilir API aracılığıyla her gün güncellenir.

Günün yeni tebrik kartı resminin yer aldığı Fugu Greetings uygulaması.
Duvar kağıdı düğmesine basıldığında günün resmi gösterilir.

Bildirim Tetikleyicileri API'sı

Bazen çok fazla ilham olsa bile, başladığınız bir karşılamayı tamamlamak için otomatik hatırlatmaya ihtiyaç duyabilirsiniz. kartını inceleyebilirsiniz. Bu, Bildirim Tetikleyicileri API'si tarafından etkinleştirilen bir özelliktir. Kullanıcı olarak, tebrik kartımı tamamlamam için otomatik hatırlatma istediğim bir zaman girebilirim. O zaman geldiğinde, tebrik kartımın sizi beklediğini belirten bir bildirim alırım.

Hedef zamanı istedikten sonra uygulama, bildirimi showTrigger ile planlar. Bu, önceden seçilmiş hedef tarihe sahip bir TimestampTrigger olabilir. Hatırlatıcı bildirimi yerel olarak tetiklenir; ağ veya sunucu tarafı gerekmez.

const targetDate = promptTargetDate();
if (targetDate) {
  const registration = await navigator.serviceWorker.ready;
  registration.showNotification('Reminder', {
    tag: 'reminder',
    body: "It's time to finish your greeting card!",
    showTrigger: new TimestampTrigger(targetDate),
  });
}

Şimdiye kadar gösterdiğim diğer her şeyde olduğu gibi bu da katılımcılara yönelik aşamalı bir geliştirmedir. bu nedenle kod yalnızca koşullu olarak yüklenir.

if ('Notification' in window && 'showTrigger' in Notification.prototype) {
  import('./notification_triggers.mjs');
}

Fugu Selamlama'da Hatırlatıcı onay kutusunu işaretlediğimde bana tebrik kartımı tamamlamam gerektiğini hatırlatıyor.

Fugu Greetings uygulamasında, kullanıcıya tebrik kartını tamamlaması gerektiğinin ne zaman hatırlatılmasını istediğini soran bir istem gösteriliyor.
Bir karşılama kartını tamamlamanız için hatırlatılacak yerel bir bildirim planlama.

Fugu Greetings'de planlanmış bir bildirim tetiklendiğinde, diğer bildirimler gibi gösteriliyor. Ancak daha önce yazdığım gibi ağ bağlantısı gerektirmez.

Fugu Greetings&#39;den tetiklenen bir bildirimi gösteren macOS Bildirim Merkezi.
Tetiklenen bildirim macOS Bildirim Merkezi'nde görünür.

Uyandırma Kilidi API'sı

Wake Lock API'yi de dahil etmek istiyorum. Bazen ilham verene kadar ekrana yeterince uzun süre bakman gerekir öpüyorum. Böyle bir durumda olabilecek en kötü şey ekranın kapanmasıdır. Wake Lock API bunun olmasını engelleyebilir.

İlk adım, navigator.wakelock.request method() ile bir uyanık kalma kilidi edinmektir. Ekran uyanık kalma kilidi elde etmek için 'screen' dizesini iletiyorum. Daha sonra, uyanık kalma kilidi serbest bırakıldığında bilgilendirilmesi için bir etkinlik işleyici ekliyorum. Örneğin, sekme görünürlüğü değiştiğinde bu durum görülebilir. Bu durumda, sekme tekrar görünür hale geldiğinde uyanık kalma kilidini yeniden alabilirim.

let wakeLock = null;
const requestWakeLock = async () => {
  wakeLock = await navigator.wakeLock.request('screen');
  wakeLock.addEventListener('release', () => {
    console.log('Wake Lock was released');
  });
  console.log('Wake Lock is active');
};

const handleVisibilityChange = () => {
  if (wakeLock !== null && document.visibilityState === 'visible') {
    requestWakeLock();
  }
};

document.addEventListener('visibilitychange', handleVisibilityChange);
document.addEventListener('fullscreenchange', handleVisibilityChange);

Evet, bu progresif bir geliştirmedir. Dolayısıyla, bunu yalnızca tarayıcı API'yi destekler.

if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
  import('./wake_lock.mjs');
}

Fugu Greetings'de, Uykusuzluk onay kutusu işaretlendiğinde ekranı uyandırdı.

Uykusuzluk onay kutusu, işaretlenirse ekranı açık tutar.
Uykusuzluk onay kutusu, uygulamayı uyanık tutar.

Idle Detection API

Bazen ekrana saatlerce baksanız bile Ancak tebrik kartınızla ilgili ne yapacağınıza dair en ufak bir fikri bile alamazsınız. Boşta Algılama API'si, uygulamanın kullanıcıların boşta kalma süresini algılamasını sağlar. Kullanıcı çok uzun süre boşta kalırsa uygulama ilk duruma sıfırlanır zemini temizler. Bu API şu anda bildirim izni, Boşta kalma algılamasının çoğu üretim kullanım alanı bildirimlerle ilgili olduğundan, örneğin, yalnızca kullanıcının aktif olarak kullandığı cihaza bildirim gönderilmesini sağlayabilirsiniz.

Bildirim izninin verildiğinden emin olduktan sonra boşta kalma algılayıcı. Boşta kalma değişikliklerini dinleyen bir etkinlik işleyici kaydediyorum. Bu değişiklikler arasında kullanıcı ve ekran durumunu değiştirebilirsiniz. Kullanıcı etkin veya boşta olabilir, ve ekran kilidi açılabilir veya kilitlenebilir. Kullanıcı boştaysa zemin temizlenir. Boşta kalma algılayıcıya 60 saniyelik eşik veriyorum.

const idleDetector = new IdleDetector();
idleDetector.addEventListener('change', () => {
  const userState = idleDetector.userState;
  const screenState = idleDetector.screenState;
  console.log(`Idle change: ${userState}, ${screenState}.`);
  if (userState === 'idle') {
    clearCanvas();
  }
});

await idleDetector.start({
  threshold: 60000,
  signal,
});

Her zamanki gibi bu kodu yalnızca tarayıcı desteklediğinde yüklüyorum.

if ('IdleDetector' in window) {
  import('./idle_detection.mjs');
}

Fugu Greetings uygulamasında Geçici onay kutusu seçiliyken tuval temizlenir kullanıcının uzun süre boşta kaldığı anlamına gelir.

Kullanıcı çok uzun süre işlem yapmadığı için temizlenmiş bir tuval içeren Fugu Greetings uygulaması.
Geçici onay kutusu işaretlendiğinde ve kullanıcı çok uzun süre boşta kaldığında tuval temizlenir.

Kapanış

Ne harika bir yarıştı. Tek bir örnek uygulamada birçok API var. Unutmayın, hiçbir zaman kullanıcının indirme maliyetini Tarayıcı'nın desteklemediği bir özelliği devre dışı bırakabilir. Progresif geliştirme kullanarak yalnızca ilgili kodun yüklenmesini sağlıyorum. HTTP/2 ile yapılan istekler ucuz olduğundan bu kalıbın birçok cihaz için uygulamalar, ancak gerçekten büyük uygulamalar için bir paketleyici kullanmayı düşünebilirsiniz.

Yalnızca geçerli tarayıcının desteklediği koda sahip dosya isteklerini gösteren Chrome Geliştirici Araçları ağ paneli.
Yalnızca geçerli tarayıcının desteklediği koda sahip dosya isteklerini gösteren Chrome Geliştirici Araçları Ağ sekmesi.

Tüm platformlar tüm özellikleri desteklemediğinden uygulama her tarayıcıda biraz farklı görünebilir. Ancak temel işlev her zaman mevcuttur; belirli bir tarayıcının özelliklerine göre kademeli olarak geliştirilir. Bu özelliklerin tek bir tarayıcıda bile değişebileceğini unutmayın. Bu, uygulamanın yüklü bir uygulama olarak mı yoksa tarayıcı sekmesinde mi çalışmasına bağlı olarak değişir.

Fugu Greetings, Android Chrome&#39;da çalışıyor ve kullanılabilen birçok özellik gösteriliyor.
Android Chrome'da çalıştırılan Fugu Greetings.
ziyaret edin.
'nı inceleyin.
Fugu Greetings masaüstü Safari&#39;de çalışıyor ve daha az kullanılabilir özellik gösteriliyor.
Masaüstü Safari'de çalıştırılan Fugu Greetings.
ziyaret edin.
'nı inceleyin.
Fugu Greetings, masaüstü Chrome&#39;da çalıştırılıyor ve kullanılabilir özelliklerin çoğu gösteriliyor.
Masaüstü Chrome'da çalıştırılan Fugu Selamları.

Fugu Greetings uygulamasıyla ilgileniyorsanız bulun ve GitHub'da çatallayın.

GitHub&#39;da Fugu Greetings deposu.
GitHub'daki Fugu Greetings uygulaması.

Chromium ekibi, gelişmiş Fugu API'leri söz konusu olduğunda çimleri daha yeşil hale getirmek için yoğun bir şekilde çalışıyor. Uygulamamın gelişiminde progresif geliştirmeler yaparak Herkesin iyi ve sağlam bir temel deneyim yaşamasını, . Uygulamalarınızda progresif geliştirmelerle neler yapacağınızı görmeyi sabırsızlıkla bekliyoruz.

Teşekkür

Christian Liebel'a minnettarım ve Fugu Greetings'e katkıda bulunan Hemanth HM. Bu makale Joe Medley ve Kayce Bask. Jake Archibald durumu öğrenmeme yardımcı oldu hizmet çalışanı bağlamında dinamik import() ile.