Modern tarayıcılar için geliştirme yapıp 2003'teymiş gibi aşamalı olarak geliştirme
Mart 2003'te Nick Finck ve Steve Champeon, web tasarım dünyasını kademeli geliştirme kavramıyla şaşırttı. Web tasarımı için bir strateji olan kademeli geliştirme, önce web sayfasının temel içeriğinin yüklenmesini vurgular ve ardından içeriğin üzerine giderek daha ayrıntılı ve teknik açıdan titiz sunum ve özellik katmanları ekler. 2003'te aşamalı geliştirme, o zamanlar modern CSS özelliklerini, göze çarpmayan JavaScript'i ve hatta ölçeklenebilir vektör grafiklerini kullanmakla ilgiliydi. 2020 ve sonrasındaki aşamalarda, aşamalı geliştirme modern tarayıcı özelliklerini kullanmakla ilgilidir.
Modern JavaScript
JavaScript'den bahsetmişken, en son temel ES 2015 JavaScript özellikleri için tarayıcı desteğinin mükemmel olduğunu belirtmek isteriz.
Yeni standart; umutlar, modüller, sınıflar, şablon literalleri, ok işlevleri, let
ve const
, varsayılan parametreler, jeneratörler, yapı bozma atama, geriye kalanlar ve yayılma, Map
/Set
, WeakMap
/WeakSet
ve daha fazlasını içerir.
Tüm tarayıcılar desteklenir.
ES 2017 özelliği olan ve en sevdiğim işlevlerden biri olan arayüz dışı işlevler, tüm popüler tarayıcılarda kullanılabilir.
async
ve await
anahtar kelimeleri, söz zincirlerinin açıkça yapılandırılması gerekmeden, söze dayalı ve eşzamansız davranışın daha net bir şekilde yazılmasını sağlar.
İsteğe bağlı zincirleme ve boş birleştirme gibi ES 2020'de eklenen yeni dil özellikleri bile çok hızlı bir şekilde destek kapsamına alındı. Aşağıda bir kod örneğini görebilirsiniz. Temel JavaScript özellikleri söz konusu olduğunda, durum bugün olduğundan çok daha iyi.
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
console.log(adventurer.dog?.name);
// Expected output: undefined
console.log(0 ?? 42);
// Expected output: 0
Örnek uygulama: Fugu Greetings
Bu makalede, Fugu Greetings (GitHub) adlı basit bir PWA kullanıyorum. Bu uygulamanın adı, web'e Android/iOS/masaüstü uygulamalarının tüm özelliklerini kazandırma çabası olan Project Fugu'ya 🐡 bir göndermedir. Proje hakkında daha fazla bilgiyi açılış sayfasında bulabilirsiniz.
Fugu Greetings, sanal tebrik kartları oluşturup sevdiklerinize göndermenize olanak tanıyan bir çizim uygulamasıdır. Bu örnek, PWA'nın temel kavramlarını göstermektedir. Güvenilir ve tamamen çevrimdışı olarak kullanılabildiği için ağ bağlantınız olmasa bile bu özelliği kullanabilirsiniz. Ayrıca cihazın ana ekranına yüklenebilir ve bağımsız bir uygulama olarak işletim sistemiyle sorunsuz şekilde entegre olur.
Progresif geliştirme
Bu bilgilerden sonra kademeli iyileştirme hakkında konuşmanın zamanı geldi. MDN Web Dokümanları Sözlüğü bu kavramı şu şekilde tanımlamaktadır:
Kademeli iyileştirme, mümkün olduğunca çok sayıda kullanıcıya temel içerik ve işlevsellik sunarken yalnızca gerekli tüm kodu çalıştırabilen en modern tarayıcıların kullanıcılarına mümkün olan en iyi deneyimi sunan bir tasarım felsefesidir.
Özellik algılama genellikle tarayıcıların daha modern işlevleri işleyip işleyemeyeceğini belirlemek için kullanılır. Çoklu doldurma ise genellikle JavaScript ile eksik özellikleri eklemek için kullanılır.
[…]
Progresif geliştirme, web geliştiricilerin mümkün olan en iyi web sitelerini geliştirirken bu web sitelerini birden fazla bilinmeyen kullanıcı aracısında çalıştırmasına olanak tanıyan faydalı bir tekniktir. Sorunsuz düşüş, kademeli iyileştirmeyle ilgili ancak aynı değildir ve genellikle kademeli iyileştirmenin tam tersi yönde bir hareket olarak görülür. Gerçekte her iki yaklaşım da geçerlidir ve genellikle birbirini tamamlayabilir.
MDN katkıda bulunanları
Her tebrik kartını sıfırdan oluşturmak gerçekten zahmetli olabilir.
Bu nedenle, kullanıcıların bir resim içe aktarmasına ve oradan başlamasına olanak tanıyan bir özellik neden olmasın?
Geleneksel yaklaşımda bunu yapmak için bir <input type=file>
öğesi kullanırdınız.
Öncelikle öğeyi oluşturur, type
değerini 'file'
olarak ayarlar ve accept
mülküne MIME türleri eklersiniz. Ardından öğeyi programatik olarak "tıklar" ve değişiklikleri beklersiniz.
Seçtiğiniz resim doğrudan kanvas üzerine 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();
});
};
İçe aktarma özelliği varsa kullanıcıların tebrik kartlarını yerel olarak kaydedebilmesi için muhtemelen bir dışa aktarma özelliği de olmalıdır.
Dosyaları kaydetmenin geleneksel yolu, download
özelliğine sahip bir ana sayfa bağlantısı oluşturmaktır. Bu bağlantının href
özelliği ise bir blob URL'sidir.
Ayrıca, indirme işlemini tetiklemek için bu URL'yi programatik olarak "tıklamanız" gerekir. Bellek sızıntısını önlemek için blob nesnesi URL'sini iptal etmeyi de 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();
};
Ama bir dakika bekleyin. Zihinsel olarak bir tebrik kartını "indirmediniz", "kaydettiniz". Tarayıcı, dosyayı nereye koyacağınızı seçmenize olanak tanıyan bir "kaydet" iletişim kutusu göstermek yerine, kullanıcı etkileşimi olmadan doğrudan tebrik kartını indirip İndirilenler klasörünüze yerleştirdi. Bu pek iyi değil.
Daha iyi bir yol olsaydı ne olurdu? Yerel bir dosyayı açıp düzenledikten sonra değişiklikleri yeni bir dosyaya veya ilk açtığınız orijinal dosyaya kaydedebilseydiniz ne olurdu? File System Access API, dosya ve dizinleri açıp oluşturmanın yanı sıra bunları değiştirmenize ve kaydetmenize olanak tanır.
Peki bir API'de nasıl özellik algılayabilirim?
File System Access API, yeni bir yöntem window.chooseFileSystemEntries()
sağlar.
Bu nedenle, bu yöntemin kullanılabilir olup olmadığına bağlı olarak farklı içe aktarma ve dışa aktarma modüllerini koşullu olarak yüklemem gerekiyor. Bunu nasıl yapacağınızı 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, aşamalı iyileştirme modelini kısaca vurgulamak isterim. Şu anda File System Access API'yi desteklemeyen tarayıcılarda eski komut dosyalarını yüklüyorum. Firefox ve Safari'nin ağ sekmelerini aşağıda görebilirsiniz.
Ancak API'yi destekleyen bir tarayıcı olan Chrome'da yalnızca yeni komut dosyaları yüklenir.
Bu, tüm modern tarayıcıların desteklediği dinamik import()
sayesinde zarif bir şekilde mümkün kılınmaktadır.
Daha önce de söylediğim gibi, bu günlerde işler oldukça yolunda.
File System Access API
Bu konuyu ele aldıktan sonra, File System Access API'ye dayalı gerçek uygulamaya bakmanın zamanı geldi.
Resim içe aktarmak için window.chooseFileSystemEntries()
işlevini çağırıyorum ve resim dosyaları istediğimi belirten bir accepts
mülkü iletiyorum.
Hem dosya uzantıları hem de MIME türleri desteklenir.
Bu işlem, getFile()
işlevini çağırarak asıl dosyayı alabileceğim bir dosya tutamacıyla sonuçlanır.
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);
}
};
Resim dışa aktarma işlemi neredeyse aynıdır ancak bu sefer chooseFileSystemEntries()
yöntemine 'save-file'
türündeki bir parametre göndermem gerekiyor.
Bu işlemden sonra bir dosya kaydetme iletişim kutusu görüyorum.
Dosya açıkken 'open-file'
varsayılan olduğu için bu gerekli değildi.
accepts
parametresini öncekine benzer şekilde ayarladım ancak bu sefer yalnızca PNG resimleriyle sınırladım.
Yine bir dosya tutamaç döndürülür ancak bu sefer dosyayı almak yerine createWritable()
işlevini çağırarak yazılabilir bir akış oluştururum.
Ardından, tebrik kartı resmim olan blob'u dosyaya yazarım.
Son olarak, yazılabilir akışı kapatıyorum.
Her şey her zaman başarısız olabilir: Diskte yer kalmamış olabilir, yazma veya okuma hatası olabilir ya da kullanıcı dosya iletişim kutusunu iptal etmiş olabilir.
Bu nedenle, çağrıları her zaman bir try...catch
ifadesi içine alırı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 aşamalı geliştirmeyi kullanarak dosyaları eskisi gibi açabilirim. İçe aktarılan dosya doğrudan tuvale çizilir. Düzenlemeler yapıp dosyanın adını ve depolama konumunu seçebileceğim gerçek bir kaydetme iletişim kutusuyla dosyaları kaydedebilirim. Dosya artık sonsuza kadar saklanacak şekilde hazırdır.
Web Share ve Web Share Target API'leri
Kartımı sonsuza kadar saklamak dışında, paylaşmak da istiyorum. Bu, Web Paylaş API ve Web Paylaş Hedef API'nin bana izin verdiği bir şeydir. Mobil ve daha yakın zamanda masaüstü işletim sistemleri yerleşik paylaşım mekanizmalarına kavuştu. Örneğin, aşağıdaki resimde masaüstü Safari'nin macOS'teki paylaşım sayfası gösterilmektedir. Bu sayfa, blog'umdaki bir makaleden tetiklenmiştir. Makaleyi Paylaş düğmesini tıkladığınızda makalenin bağlantısını bir arkadaşınızla (ör. macOS Mesajlar uygulaması üzerinden) paylaşabilirsiniz.
Bunu gerçekleştirmek için kullanacağınız kod oldukça basittir. navigator.share()
işlevini çağırıyorum ve bir nesnede isteğe bağlı title
, text
ve url
parametrelerini ilerliyorum.
Peki bir resim eklemek istersem ne olur? Web Share API'nin 1. düzeyi henüz bunu desteklemiyor.
Neyse ki Web Paylaşımı 2. Seviye, dosya paylaşımı özellikleri ekledi.
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 yapacağınızı göstereyim.
Öncelikle, bir blobdan oluşan bir files
dizisi içeren bir data
nesnesi, ardından bir title
ve bir text
hazırlamam gerekiyor. Ardından, en iyi uygulama olarak, adından da anlaşılacağı gibi navigator.canShare()
yöntemini kullanırım. Bu yöntem, paylaşmaya çalıştığım data
nesnenin teknik olarak tarayıcı tarafından paylaşılıp paylaşılamayacağı konusunda bana bilgi verir.
navigator.canShare()
, verilerin paylaşılabileceğini söylerse navigator.share()
'u aramaya hazırım.
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, aşamalı geliştirmeyi kullanıyorum.
navigator
nesnesinde hem 'share'
hem de 'canShare'
varsa ancak o zaman devam edip share.mjs
'ı dinamik import()
aracılığıyla yüklerim.
Mobil Safari gibi, iki koşuldan yalnızca birini karşılayan tarayıcılarda işlevi yüklemem.
const loadShare = () => {
if ('share' in navigator && 'canShare' in navigator) {
import('./share.mjs');
}
};
Fugu Greetings'da, Android'de Chrome gibi desteklenen bir tarayıcıda Paylaş düğmesine dokunursam yerleşik paylaşım sayfası açılır. Örneğin, Gmail'i seçtiğimde e-posta oluşturucu widget'ı, resmi ekleyerek açılır.
Contact Picker API
Ardından, cihazın adres defteri veya kişi yöneticisi uygulaması olan kişilerden bahsetmek istiyorum. Bir tebrik kartı yazarken kişinin adını doğru yazmak her zaman kolay olmayabilir. Örneğin, adının Kiril alfabesiyle yazılmasını tercih eden bir arkadaşım var. Almanca QWERTZ klavye kullanıyorum ve adlarını nasıl yazacağımı bilmiyorum. Bu, Contact Picker API'nin çözebileceği bir sorundur. Arkadaşımı telefonumun Kişiler uygulamasında sakladığım için Kişi Seçici API'si aracılığıyla web'den kişilerime erişebiliyorum.
Öncelikle, erişmek istediğim mülklerin listesini belirtmem gerekiyor.
Bu durumda yalnızca adları istiyorum ancak diğer kullanım alanları için telefon numaraları, e-postalar, avatar simgeleriyle fiziksel adresler de ilgimi çekebilir.
Ardından, birden fazla giriş seçebilmek için bir options
nesnesi yapılandırıp multiple
değerini true
olarak ayarlıyorum.
Son olarak, kullanıcı tarafından seçilen kişiler için istenen özellikleri döndüren navigator.contacts.select()
işlevini çağırabilirim.
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);
}
};
Şimdiye kadar muhtemelen şu kalıbı öğrendiniz: Yalnızca API gerçekten desteklendiğinde dosyayı yüklerim.
if ('contacts' in navigator) {
import('./contacts.mjs');
}
Fugu Greeting'de Kişiler düğmesine dokunup en iyi iki arkadaşım Сергей Михайлович Брин ve 劳伦斯·爱德华·"拉里"·佩奇'yi seçtiğimde, kişiler seçicinin yalnızca adlarını göstermekle sınırlı olduğunu, e-posta adreslerini veya telefon numaraları gibi diğer bilgileri göstermediğini görebilirsiniz. Ardından, isimlerini tebrik kartıma çiziyorum.
Eşzamansız Clipboard API
Bir sonraki konu kopyalama ve yapıştırma. Yazılım geliştiricileri olarak en sevdiğimiz işlemlerden biri kopyalama ve yapıştırmadır. Kart yazarı olarak bazen aynısını yapmak isterim. Üzerinde çalıştığım bir tebrik kartına resim yapıştırmak veya tebrik kartımı başka bir yerden düzenlemeye devam edebilmek için kopyalamak istiyorum. Async Clipboard API hem metni hem de görselleri destekler. Fugu Greetings uygulamasına kopyalama ve yapıştırma desteğini nasıl eklediğimi adım adım anlatacağım.
Bir şeyi sistemin panosuna kopyalamak için panoya yazmam gerekiyor.
navigator.clipboard.write()
yöntemi, parametre olarak bir pano öğesi dizisi alır.
Her panoya kopyalanan öğe, temelde değeri bir blob, anahtarı ise blob türü olan bir nesnedir.
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 navigator.clipboard.read()
çağrısını kullanarak elde ettiğim pano öğelerini döngüde incelemem gerekiyor.
Bunun nedeni, farklı temsillerde birden fazla pano öğesinin panoda bulunabilmesidir.
Her panoya alınan öğenin, mevcut kaynakların MIME türlerini belirten bir types
alanı vardır.
Daha önce aldığım MIME türünü ileterek, panosundaki öğenin getType()
yöntemini çağırıyorum.
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);
}
};
Bu konuda söylenecek çok şey var. Bu işlemi yalnızca desteklenen tarayıcılarda yaparım.
if ('clipboard' in navigator && 'write' in navigator.clipboard) {
import('./clipboard.mjs');
}
Peki bu nasıl çalışır? macOS Önizleme uygulamasında açık bir resmim var ve bu resmi panoya kopyalıyorum. Yapıştır'ı tıkladığımda Fugu Greetings uygulaması, uygulamanın panodaki metin ve görselleri görmesine izin vermek isteyip istemediğimi sorar.
Son olarak, izin kabul edildikten sonra resim uygulamaya yapıştırılır. Bunun tersi de geçerlidir. Bir tebrik kartını panoya kopyalayacağım. Ardından Önizleme'yi açıp Dosya'yı ve ardından Kopya Kutusundan Yeni'yi tıkladığımda tebrik kartı, yeni bir adsız resme yapıştırılıyor.
Badging API
Badging API de faydalı API'lerden biridir.
Yüklenebilir bir PWA olarak Fugu Greetings'in, kullanıcıların uygulama paneline veya ana ekrana yerleştirebileceği bir uygulama simgesi vardır.
API'yi göstermenin eğlenceli ve kolay bir yolu, Fugu Greetings'da kalem vuruşları sayacı olarak kullanmaktır.
pointerdown
etkinliği gerçekleştiğinde kalem vuruşları sayacını artıran ve ardından güncellenmiş simge rozetini ayarlayan bir etkinlik işleyici ekledim.
Tuval her 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 aşamalı bir geliştirme olduğundan yükleme mantığı her zamanki gibidir.
if ('setAppBadge' in navigator) {
import('./badge.mjs');
}
Bu örnekte, her sayı için bir kalem darbesi kullanarak bir ile yedi arasındaki sayıları çizdim. Simgedeki rozet sayacı artık yedi.
Periyodik Arka Plan Senkronizasyonu API'si
Her güne yeni bir şeyle başlamak ister misiniz? Fugu Greetings uygulamasının güzel özelliklerinden biri, her sabah yeni bir arka plan resmiyle sizi selam kartınıza başlamaya teşvik etmesidir. Uygulama, bunu yapmak için Periodic Background Sync API'yi kullanır.
İlk adım, hizmet çalışanı kaydına düzenli senkronizasyon etkinliği kaydetmektir.
'image-of-the-day'
adlı bir senkronizasyon etiketini dinler ve en az bir günlük aralığa sahiptir. 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ğine kulak vermek olacaktır.
Etkinlik etiketi 'image-of-the-day'
ise (yani daha önce kaydedilen etiket) günün resmi getImageOfTheDay()
işlevi aracılığıyla alınır ve sonuç tüm istemcilere dağıtılır. Böylece istemciler tuvallerini ve önbelleğe alınmış verilerini güncelleyebilir.
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 yine aşamalı bir geliştirmedir. Bu nedenle kod yalnızca API tarayıcı tarafından desteklendiğinde yüklenir.
Bu durum hem istemci kodu hem de hizmet çalışanı kodu için geçerlidir.
Desteklenmeyen tarayıcılarda bu iki dosya da yüklenmez.
Hizmet çalışanında dinamik import()
(hizmet çalışanı bağlamında henüz desteklenmiyor) yerine klasik importScripts()
kullanacağımı unutmayın.
// 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 Greetings'da Duvar kağıdı düğmesine bastığınızda, Düzenli Arka Plan Senkronizasyonu API'si aracılığıyla her gün güncellenen günün tebrik kartı resmi gösterilir.
Notification Triggers API
Bazen çok fazla ilham kaynağına sahip olsanız bile, başladığınız tebrik kartını bitirmek için bir dürtüye ihtiyacınız olur. Bu özellik, Notification Triggers API tarafından etkinleştirilir. Kullanıcı olarak, tebrik kartımı bitirmem için hatırlatılmak istediğim zamanı girebilirim. Bu zaman geldiğinde, tebrik kartımın beklediğine dair bir bildirim alacağım.
Hedef zamanın istenmesinden sonra uygulama, bildirimi showTrigger
ile planlar.
Bu, önceden seçilen hedef tarihi içeren bir TimestampTrigger
olabilir.
Hatırlatıcı bildirimi yerel olarak tetiklenir. Ağ veya sunucu tarafına gerek yoktur.
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 her şeyde olduğu gibi, bu da 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 Greetings'da Hatırlatıcı onay kutusunu işaretlediğimde, karşılama kartımı tamamlamam gerektiğini ne zaman hatırlatılmasını istediğimi soran bir istem gösteriliyor.
Fugu Greetings'da planlanmış bir bildirim tetiklendiğinde, diğer bildirimler gibi gösterilir ancak daha önce de belirttiğim gibi ağ bağlantısı gerektirmez.
Wake Lock API
Wake Lock API'yi de dahil etmek istiyorum. Bazen ilham size gelene kadar ekrana uzun süre bakmanız yeterlidir. Bu durumda en kötü ihtimal ekranın kapanmasıdır. Wake Lock API, bunun olmasını önleyebilir.
İlk adım, navigator.wakelock.request method()
ile uyanma kilidi elde etmektir.
Ekran uyanık kalma kilidi almak için 'screen'
dizesini iletirim.
Ardından, uyanma kilidi açıldığında bilgilendirilmek için bir etkinlik işleyici eklerim.
Örneğin, sekme görünürlüğü değiştiğinde bu durumla karşılaşabilirsiniz.
Bu durumda, sekme tekrar görünür hale geldiğinde uyanma kilidini yeniden edinebilirim.
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 aşamalı bir geliştirmedir. Bu nedenle, yalnızca tarayıcı API'yi desteklediğinde yüklemem gerekir.
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
import('./wake_lock.mjs');
}
Fugu Greetings'te, işaretlendiğinde ekranı açık tutan bir Uykusuzluk onay kutusu bulunur.
Idle Detection API
Bazen saatlerce ekrana baksanız bile hiçbir şey aklınıza gelmez ve tebrik kartınızla ne yapacağınızı bilemezsiniz. Idle Detection API, uygulamanın kullanıcının boşta kalma süresini algılamasına olanak tanır. Kullanıcı uzun süre boyunca işlem yapmazsa uygulama ilk duruma sıfırlanır ve tuval temizlenir. Boş ekran algılamanın birçok üretim kullanım alanı bildirimlerle ilgili olduğundan (ör. yalnızca kullanıcının o anda etkin olarak kullandığı bir cihaza bildirim göndermek için) bu API şu anda bildirim izni kapsamındadır.
Bildirim izninin verildiğinden emin olduktan sonra boşta kalma algılayıcısını örneklendiririm. Kullanıcıyı ve ekran durumunu içeren boşta kalma değişikliklerini dinleyen bir etkinlik işleyici kaydediyorum. Kullanıcı etkin veya etkin olmayan durumda olabilir ve ekran kilitli veya kilidi açık olabilir. Kullanıcı işlem yapmazsa tuval temizlenir. Boşta kalma algılayıcısına 60 saniyelik bir eşik değeri 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 zaman olduğu gibi, bu kodu yalnızca tarayıcı desteklediğinde yüklerim.
if ('IdleDetector' in window) {
import('./idle_detection.mjs');
}
Fugu Greetings uygulamasında, Geçici onay kutusu işaretlendiğinde ve kullanıcı çok uzun süre hareketsiz kaldığında tuval temizlenir.
Kapanış
Vay canına. Yalnızca bir örnek uygulamada bu kadar çok API var. Ayrıca, tarayıcısının desteklemediği bir özelliğin indirme maliyetini hiçbir zaman kullanıcıdan ödemesini istemem. Progresif geliştirmeyi kullanarak yalnızca alakalı kodun yüklenmesini sağlıyorum. HTTP/2'de istekler ucuz olduğundan bu kalıp birçok uygulama için işe yarayabilir. Ancak gerçekten büyük uygulamalar için bir paketleyici kullanmayı düşünebilirsiniz.
Tüm platformlar tüm özellikleri desteklemediğinden uygulama her tarayıcıda biraz farklı görünebilir. Ancak temel işlevler her zaman mevcuttur ve ilgili tarayıcının özelliklerine göre aşamalı olarak geliştirilir. Bu özelliklerin, uygulamanın yüklü bir uygulama olarak mı yoksa tarayıcı sekmesinde mi çalıştığına bağlı olarak aynı tarayıcıda bile değişebileceğini unutmayın.
Fugu Greetings uygulamasıyla ilgileniyorsanız GitHub'da bu uygulamayı alıp çatallayın.
Chromium ekibi, gelişmiş Fugu API'leri konusunda daha iyi bir deneyim sunmak için yoğun şekilde çalışıyor. Uygulamamı geliştirirken aşamalı geliştirmeyi uygulayarak herkesin iyi ve sağlam bir temel deneyim elde etmesini sağlarken daha fazla web platformu API'sini destekleyen tarayıcılar kullanan kullanıcıların daha da iyi bir deneyim elde etmesini sağlıyorum. Uygulamalarınızda aşamalı geliştirme özelliğini nasıl kullanacağınızı görmeyi sabırsızlıkla bekliyorum.
Teşekkür ederiz
Fugu Greetings'e katkıda bulunan Christian Liebel ve Hemanth HM'ye teşekkür ederim.
Bu makale Joe Medley ve Kayce Basques tarafından incelenmiştir.
Jake Archibald, hizmet çalışanı bağlamında dinamik import()
ile ilgili durumu öğrenmeme yardımcı oldu.