Membangun untuk browser modern dan meningkatkan kualitasnya secara bertahap seperti pada tahun 2003
Pada Maret 2003, Nick Finck dan Steve Champeon mengejutkan dunia desain web dengan konsep progressive enhancement, strategi untuk desain web yang menekankan pemuatan konten halaman web inti terlebih dahulu, lalu secara bertahap menambahkan lapisan presentasi dan fitur yang lebih nuansatu dan secara teknis ketat di atas konten. Sementara pada tahun 2003, progressive enhancement adalah tentang penggunaan—pada saat itu—fitur CSS modern, JavaScript yang tidak mengganggu, dan bahkan hanya Scalable Vector Graphics. Progressive enhancement pada tahun 2020 dan seterusnya adalah tentang penggunaan kemampuan browser modern.
JavaScript Modern
Berbicara tentang JavaScript, situasi dukungan browser untuk fitur JavaScript core ES 2015 terbaru
sangat bagus.
Standar baru ini mencakup promise, modul, class, literal template, fungsi panah, let
dan const
,
parameter default, generator, penetapan destrukturisasi, rest dan spread, Map
/Set
,
WeakMap
/WeakSet
, dan banyak lagi.
Semua didukung.
Fungsi asinkron, fitur ES 2017, dan salah satu favorit saya,
dapat digunakan
di semua browser utama.
Kata kunci async
dan await
memungkinkan perilaku asinkron berbasis promise
ditulis dalam gaya yang lebih bersih, sehingga tidak perlu mengonfigurasi rantai promise secara eksplisit.
Bahkan penambahan bahasa ES 2020 yang sangat baru seperti chaining opsional dan penggabungan nullish telah mendapatkan dukungan dengan sangat cepat. Anda dapat melihat contoh kode di bawah. Dalam hal fitur JavaScript inti, tidak ada yang lebih baik dari yang ada saat ini.
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
console.log(adventurer.dog?.name);
// Expected output: undefined
console.log(0 ?? 42);
// Expected output: 0
Aplikasi contoh: Fugu Greetings
Untuk artikel ini, saya menggunakan PWA sederhana, yang disebut Fugu Greetings (GitHub). Nama aplikasi ini adalah penghargaan untuk Project Fugu 🐡, sebuah upaya untuk memberi web semua kemampuan aplikasi Android/iOS/desktop. Anda dapat membaca selengkapnya tentang project ini di halaman landingnya.
Fugu Greetings adalah aplikasi menggambar yang memungkinkan Anda membuat kartu ucapan virtual dan mengirimkannya kepada orang-orang terkasih. Contoh ini menunjukkan konsep inti PWA. Server ini dapat diandalkan dan sepenuhnya aktif secara offline, sehingga meskipun tidak memiliki jaringan, Anda masih dapat menggunakannya. Aplikasi ini juga Dapat diinstal ke layar utama perangkat dan terintegrasi dengan lancar dengan sistem operasi sebagai aplikasi mandiri.
Peningkatan progresif
Setelah itu, saatnya membahas progressive enhancement. Glosarium MDN Web Docs menentukan konsep sebagai berikut:
Progressive enhancement adalah filosofi desain yang memberikan dasar konten dan fungsi penting kepada sebanyak mungkin pengguna, sekaligus memberikan pengalaman terbaik hanya kepada pengguna browser paling modern yang dapat menjalankan semua kode yang diperlukan.
Deteksi fitur umumnya digunakan untuk menentukan apakah browser dapat menangani fungsi yang lebih modern, sedangkan polyfill sering digunakan untuk menambahkan fitur yang tidak ada dengan JavaScript.
[…]
Peningkatan progresif adalah teknik yang berguna yang memungkinkan developer web berfokus pada pengembangan situs terbaik sekaligus membuat situs tersebut berfungsi di beberapa agen pengguna yang tidak dikenal. Degradasi halus berkaitan, tetapi bukan hal yang sama dan sering dilihat sebagai arah yang berlawanan menuju {i>progressive enhancement<i}. Pada kenyataannya, kedua pendekatan tersebut valid dan sering kali dapat saling melengkapi.
Kontributor MDN
Memulai setiap kartu ucapan dari awal bisa sangat rumit.
Jadi, mengapa tidak memiliki fitur yang memungkinkan pengguna mengimpor gambar, dan memulai dari sana?
Dengan pendekatan tradisional, Anda akan menggunakan elemen
<input type=file>
untuk mewujudkannya.
Pertama, Anda akan membuat elemen, menetapkan type
ke 'file'
, dan menambahkan jenis MIME ke properti accept
,
lalu "mengklik"nya secara terprogram dan memproses perubahan.
Saat Anda memilih gambar, gambar tersebut akan langsung diimpor ke kanvas.
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();
});
};
Jika ada fitur impor, mungkin harus ada fitur ekspor
sehingga pengguna dapat menyimpan kartu ucapan secara lokal.
Cara tradisional untuk menyimpan file adalah dengan membuat link anchor
dengan atribut download
dan dengan URL blob sebagai href
-nya.
Anda juga akan "mengklik"nya secara terprogram untuk memicu download,
dan, untuk mencegah kebocoran memori, jangan lupa untuk mencabut URL objek blob.
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();
};
Tapi tunggu sebentar. Secara mental, Anda belum "mendownload" kartu ucapan, Anda telah "menyimpan"nya. Bukannya menampilkan dialog "simpan" yang memungkinkan Anda memilih tempat meletakkan file, browser langsung mendownload kartu ucapan tanpa interaksi pengguna dan telah memasukkannya langsung ke folder Download. Ini tidak bagus.
Bagaimana jika ada cara yang lebih baik? Bagaimana jika Anda dapat membuka file lokal, mengeditnya, lalu menyimpan perubahan, baik ke file baru, atau kembali ke file asli yang awalnya Anda buka? Ternyata ada. File System Access API memungkinkan Anda membuka dan membuat file serta direktori, serta mengubah dan menyimpannya.
Jadi, bagaimana cara mendeteksi fitur API?
File System Access API mengekspos metode baru window.chooseFileSystemEntries()
.
Akibatnya, saya perlu memuat modul impor dan ekspor yang berbeda secara kondisional bergantung pada apakah metode ini tersedia atau tidak. Kami telah menunjukkan cara melakukannya di bawah.
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'),
]);
}
};
Namun, sebelum membahas detail File System Access API, izinkan saya menyoroti pola progressive enhancement di sini. Di browser yang saat ini tidak mendukung File System Access API, saya memuat skrip lama. Anda dapat melihat tab jaringan Firefox dan Safari di bawah.
Namun, di Chrome, browser yang mendukung API, hanya skrip baru yang dimuat.
Hal ini dapat dilakukan dengan mudah berkat
import()
dinamis, yang
didukung oleh semua browser modern.
Seperti yang saya katakan sebelumnya, rumput saat ini cukup hijau.
File System Access API
Setelah saya mengatasi masalah ini, kini saatnya melihat implementasi sebenarnya berdasarkan File System Access API.
Untuk mengimpor gambar, saya memanggil window.chooseFileSystemEntries()
dan meneruskan properti accepts
tempat saya menyatakan bahwa saya menginginkan file gambar.
Ekstensi file dan jenis MIME didukung.
Tindakan ini menghasilkan handle file, tempat saya bisa mendapatkan file sebenarnya dengan memanggil getFile()
.
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);
}
};
Mengekspor gambar hampir sama, tetapi kali ini
Saya harus meneruskan parameter jenis 'save-file'
ke metode chooseFileSystemEntries()
.
Dari sini, saya mendapatkan dialog simpan file.
Dengan file terbuka, hal ini tidak diperlukan karena 'open-file'
adalah default.
Saya menetapkan parameter accepts
seperti sebelumnya, tetapi kali ini hanya terbatas pada gambar PNG.
Sekali lagi, saya mendapatkan kembali handle file, tetapi bukan mendapatkan file,
kali ini saya membuat aliran yang dapat ditulis dengan memanggil createWritable()
.
Selanjutnya, saya menulis blob, yang merupakan gambar kartu ucapan saya, ke file tersebut.
Terakhir, saya menutup aliran data yang dapat ditulis.
Semuanya dapat gagal: Disk mungkin kehabisan ruang,
mungkin ada error tulis atau baca, atau mungkin pengguna membatalkan dialog file.
Itulah sebabnya saya selalu menggabungkan panggilan dalam pernyataan try...catch
.
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);
}
};
Dengan menggunakan progressive enhancement dengan File System Access API, Saya dapat membuka file seperti sebelumnya. File yang diimpor akan langsung digambar ke kanvas. Saya dapat melakukan pengeditan dan akhirnya menyimpannya dengan kotak dialog simpan yang sebenarnya tempat saya dapat memilih nama dan lokasi penyimpanan file. Sekarang file siap disimpan selamanya.
Web Share dan Web Share Target API
Selain menyimpannya selamanya, mungkin saya ingin membagikan kartu ucapan saya. Hal ini dapat dilakukan dengan Web Share API dan Web Share Target API. Sistem operasi seluler, dan baru-baru ini desktop, telah mendapatkan mekanisme berbagi bawaan. Misalnya, di bawah ini adalah sheet berbagi Safari desktop di macOS yang dipicu dari artikel di blog saya. Saat mengklik tombol Bagikan Artikel, Anda dapat membagikan link ke artikel kepada teman, misalnya, melalui aplikasi Message macOS.
Kode untuk melakukannya cukup sederhana. Saya memanggil navigator.share()
dan
meneruskan title
, text
, dan url
opsional dalam objek.
Namun, bagaimana jika saya ingin melampirkan gambar? Level 1 dari Web Share API belum mendukung fitur ini.
Kabar baiknya, Berbagi Web Tingkat 2 telah menambahkan kemampuan berbagi file.
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);
}
Mari kita pelajari cara membuatnya berfungsi dengan aplikasi kartu Ucapan Fugu.
Pertama, saya perlu menyiapkan objek data
dengan array files
yang terdiri dari satu blob, lalu
title
dan text
. Selanjutnya, sebagai praktik terbaik, saya menggunakan metode navigator.canShare()
baru yang melakukan
apa yang disarankan namanya:
Metode ini memberi tahu saya apakah objek data
yang saya coba bagikan secara teknis dapat dibagikan oleh browser.
Jika navigator.canShare()
memberi tahu saya bahwa data dapat dibagikan, saya siap
memanggil navigator.share()
seperti sebelumnya.
Karena semuanya dapat gagal, saya kembali menggunakan blok try...catch
.
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);
}
};
Seperti sebelumnya, saya menggunakan
{i>progressive enhancement<i}.
Jika 'share'
dan 'canShare'
ada di objek navigator
, barulah saya melanjutkan dan
memuat share.mjs
melalui import()
dinamis.
Di browser seperti Safari seluler yang hanya memenuhi salah satu dari dua kondisi, saya tidak memuat
fungsi tersebut.
const loadShare = () => {
if ('share' in navigator && 'canShare' in navigator) {
import('./share.mjs');
}
};
Di Fugu Greetings, jika saya mengetuk tombol Bagikan di browser pendukung seperti Chrome di Android, lembar berbagi bawaan akan terbuka. Misalnya, saya dapat memilih Gmail, dan widget penulis email akan muncul dengan gambar yang dilampirkan.
Contact Picker API
Selanjutnya, saya ingin membahas kontak, yang berarti buku alamat atau aplikasi pengelola kontak perangkat. Saat Anda menulis kartu ucapan, mungkin tidak selalu mudah untuk menulis nama seseorang dengan benar. Misalnya, saya memiliki teman bernama Sergey yang lebih suka namanya dieja dalam huruf Sirilik. Saya menggunakan keyboard QWERTZ Jerman dan tidak tahu cara mengetik namanya. Ini adalah masalah yang dapat dipecahkan oleh Contact Picker API. Karena saya menyimpan teman saya di aplikasi kontak ponsel, melalui Contacts Picker API, saya dapat mengakses kontak saya dari web.
Pertama, saya perlu menentukan daftar properti yang ingin diakses.
Dalam hal ini, saya hanya ingin nama,
tetapi untuk kasus penggunaan lainnya, saya mungkin tertarik dengan nomor telepon, email, ikon avatar,
atau alamat fisik.
Selanjutnya, saya mengonfigurasi objek options
dan menetapkan multiple
ke true
, sehingga saya dapat memilih lebih dari satu entri.
Terakhir, saya dapat memanggil navigator.contacts.select()
, yang menampilkan properti yang diinginkan
untuk kontak yang dipilih pengguna.
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);
}
};
Dan sekarang Anda mungkin telah mempelajari polanya: Saya hanya memuat file saat API benar-benar didukung.
if ('contacts' in navigator) {
import('./contacts.mjs');
}
Di Fugu Greeting, saat saya mengetuk tombol Kontak dan memilih dua sahabat saya, Сергей Михайлович Брин dan 劳伦斯·爱德华·"拉里"·佩奇, Anda dapat melihat cara pemilih kontak dibatasi untuk hanya menampilkan nama mereka, tetapi tidak menampilkan alamat email mereka, atau informasi lain seperti nomor telepon mereka. Nama mereka kemudian digambar di kartu ucapan saya.
Asynchronous Clipboard API
Berikutnya adalah menyalin dan menempel. Salah satu operasi favorit kami sebagai developer software adalah salin dan tempel. Sebagai penulis kartu ucapan, terkadang saya ingin melakukan hal yang sama. Saya mungkin ingin menempelkan gambar ke kartu ucapan yang sedang saya kerjakan, atau menyalin kartu ucapan agar dapat terus mengeditnya dari tempat lain. Asynchronous Clipboard API, mendukung teks dan gambar. Saya akan memandu Anda terkait cara menambahkan dukungan salin dan tempel ke aplikasi Fugu Greetings.
Untuk menyalin sesuatu ke papan klip sistem, saya harus menulis ke papan klip tersebut.
Metode navigator.clipboard.write()
menggunakan array item papan klip sebagai
parameter.
Setiap item papan klip pada dasarnya adalah objek dengan blob sebagai nilai, dan jenis blob
sebagai kunci.
const copy = async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
} catch (err) {
console.error(err.name, err.message);
}
};
Untuk menempelkan, saya perlu melakukan loop pada item papan klip yang saya dapatkan dengan memanggil
navigator.clipboard.read()
.
Alasannya adalah karena beberapa item papan klip mungkin berada di papan klip dalam
representasi yang berbeda.
Setiap item papan klip memiliki kolom types
yang memberi tahu saya jenis MIME dari resource
yang tersedia.
Saya memanggil metode getType()
item papan klip, dengan meneruskan
jenis MIME yang saya peroleh sebelumnya.
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);
}
};
Dan sekarang hampir tidak perlu untuk dikatakan. Saya hanya melakukannya di browser yang mendukung.
if ('clipboard' in navigator && 'write' in navigator.clipboard) {
import('./clipboard.mjs');
}
Jadi, bagaimana cara kerjanya dalam praktik? Saya membuka gambar di aplikasi Pratinjau macOS dan menyalinnya ke papan klip. Saat saya mengklik Tempel, aplikasi Fugu Greetings akan menanyakan apakah saya ingin mengizinkan aplikasi melihat teks dan gambar di papan klip.
Terakhir, setelah menerima izin, gambar akan ditempelkan ke dalam aplikasi. Hal sebaliknya juga berlaku. Izinkan saya menyalin kartu ucapan ke papan klip. Saat saya membuka Pratinjau dan mengklik File, lalu New from Clipboard, kartu ucapan akan ditempelkan ke gambar baru tanpa judul.
Badging API
API lain yang berguna adalah Badging API.
Sebagai PWA yang dapat diinstal, Fugu Greetings tentu saja memiliki ikon aplikasi
yang dapat ditempatkan pengguna di dok aplikasi atau layar utama.
Cara yang menyenangkan dan mudah untuk mendemonstrasikan API adalah dengan (salah) menggunakannya di Fugu Greetings
sebagai penghitung goresan pena.
Saya telah menambahkan pemroses peristiwa yang menambahkan penghitung goresan pena setiap kali peristiwa pointerdown
terjadi,
lalu menetapkan badge ikon yang diperbarui.
Setiap kali kanvas dihapus, penghitung akan direset, dan badge akan dihapus.
let strokes = 0;
canvas.addEventListener('pointerdown', () => {
navigator.setAppBadge( strokes);
});
clearButton.addEventListener('click', () => {
strokes = 0;
navigator.setAppBadge(strokes);
});
Fitur ini adalah peningkatan progresif, sehingga logika pemuatan seperti biasa.
if ('setAppBadge' in navigator) {
import('./badge.mjs');
}
Dalam contoh ini, saya telah menggambar angka dari satu hingga tujuh, menggunakan satu goresan pena per angka. Penghitung badge di ikon kini berjumlah tujuh.
Periodic Background Sync API
Ingin memulai setiap hari dengan sesuatu yang baru? Fitur menarik dari aplikasi Fugu Greetings adalah aplikasi ini dapat menginspirasi Anda setiap pagi dengan gambar latar baru untuk memulai kartu ucapan. Aplikasi menggunakan Periodic Background Sync API untuk mencapai hal ini.
Langkah pertama adalah mendaftarkan peristiwa sinkronisasi berkala dalam pendaftaran pekerja layanan.
Fungsi ini memproses tag sinkronisasi yang disebut 'image-of-the-day'
dan memiliki interval minimum satu hari,
sehingga pengguna bisa mendapatkan gambar latar baru setiap 24 jam.
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);
}
};
Langkah kedua adalah memproses peristiwa periodicsync
di pekerja layanan.
Jika tag peristiwa adalah 'image-of-the-day'
, yaitu yang terdaftar sebelumnya, gambar hari ini akan diambil melalui fungsi getImageOfTheDay()
, dan hasilnya disebarkan ke semua klien sehingga mereka dapat memperbarui kanvas dan cache mereka.
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,
});
});
})()
);
}
});
Sekali lagi, ini benar-benar merupakan peningkatan progresif, sehingga kode hanya dimuat jika
API didukung oleh browser.
Hal ini berlaku untuk kode klien dan kode pekerja layanan.
Pada browser yang tidak mendukung, tidak satu pun yang dimuat.
Perhatikan bagaimana di pekerja layanan, bukan import()
dinamis
(yang belum didukung dalam konteks pekerja layanan),
saya menggunakan importScripts()
klasik.
// 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');
}
Di Fugu Greetings, menekan tombol Wallpaper akan menampilkan gambar kartu ucapan hari ini yang diperbarui setiap hari melalui Periodic Background Sync API.
API Pemicu Notifikasi
Terkadang, meskipun memiliki banyak inspirasi, Anda memerlukan dorongan untuk menyelesaikan kartu ucapan yang telah dimulai. Ini adalah fitur yang diaktifkan oleh Notification Triggers API. Sebagai pengguna, saya dapat memasukkan waktu yang saya inginkan untuk mendapatkan notifikasi agar menyelesaikan kartu ucapan. Saat tiba waktunya, saya akan mendapatkan notifikasi bahwa kartu ucapan saya sudah siap.
Setelah meminta waktu target,
aplikasi menjadwalkan notifikasi dengan showTrigger
.
Ini dapat berupa TimestampTrigger
dengan tanggal target yang dipilih sebelumnya.
Notifikasi pengingat akan dipicu secara lokal, tidak diperlukan sisi jaringan atau server.
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),
});
}
Seperti semua hal yang telah saya tunjukkan sejauh ini, ini adalah peningkatan progresif, sehingga kode hanya dimuat secara kondisional.
if ('Notification' in window && 'showTrigger' in Notification.prototype) {
import('./notification_triggers.mjs');
}
Saat mencentang kotak Pengingat di Salam Fugu, dialog akan menanyakan kapan saya ingin diingatkan untuk menyelesaikan kartu ucapan.
Saat notifikasi terjadwal dipicu di Fugu Greetings, notifikasi akan ditampilkan seperti notifikasi lainnya, tetapi seperti yang saya tulis sebelumnya, notifikasi tidak memerlukan koneksi jaringan.
Wake Lock API
Saya juga ingin menyertakan Wake Lock API. Terkadang Anda hanya perlu menatap layar cukup lama hingga inspirasi datang. Kemungkinan terburuk yang dapat terjadi adalah layar akan mati. Wake Lock API dapat mencegah hal ini terjadi.
Langkah pertama adalah mendapatkan penguncian layar saat aktif dengan navigator.wakelock.request method()
.
Saya meneruskan string 'screen'
untuk mendapatkan kunci layar saat aktif.
Kemudian, saya menambahkan pemroses peristiwa untuk diberi tahu saat kunci layar aktif dilepaskan.
Hal ini dapat terjadi, misalnya, saat visibilitas tab berubah.
Jika ini terjadi, saya bisa mendapatkan kembali penguncian layar saat aktif saat tab terlihat lagi.
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);
Ya, ini adalah peningkatan progresif, jadi saya hanya perlu memuatnya saat browser mendukung API.
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
import('./wake_lock.mjs');
}
Di Salam Fugu, ada kotak centang Insomnia yang, saat dicentang, akan membuat layar tetap aktif.
Idle Detection API
Terkadang, meskipun Anda menatap layar selama berjam-jam, hal itu tidak berguna dan Anda tidak dapat menemukan ide sedikit pun tentang apa yang harus dilakukan dengan kartu ucapan Anda. Idle Detection API memungkinkan aplikasi mendeteksi waktu tidak ada aktivitas pengguna. Jika pengguna tidak ada aktivitas terlalu lama, aplikasi akan direset ke status awal dan menghapus kanvas. API ini saat ini dibatasi oleh izin notifikasi, karena banyak kasus penggunaan produksi deteksi tidak ada aktivitas yang terkait dengan notifikasi, misalnya, hanya mengirim notifikasi ke perangkat yang saat ini digunakan secara aktif oleh pengguna.
Setelah memastikan izin notifikasi diberikan, saya akan membuat instance detektor tidak ada aktivitas. Saya mendaftarkan pemroses peristiwa yang memproses perubahan tidak ada aktivitas, yang mencakup pengguna dan status layar. Pengguna dapat menjadi aktif atau tidak ada aktivitas, dan layar dapat dibuka kuncinya atau dikunci. Jika pengguna tidak ada aktivitas, kanvas akan dihapus. Saya memberikan batas waktu 60 detik ke detektor tidak ada aktivitas.
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,
});
Dan seperti biasa, saya hanya memuat kode ini jika browser mendukungnya.
if ('IdleDetector' in window) {
import('./idle_detection.mjs');
}
Di aplikasi Fugu Greetings, kanvas akan dibersihkan jika kotak centang Ephemeral dicentang dan pengguna tidak melakukan aktivitas terlalu lama.
Penutup
Fiuh, perjalanan yang luar biasa. Begitu banyak API hanya dalam satu aplikasi contoh. Dan, ingat, saya tidak pernah mengharuskan pengguna membayar biaya download untuk fitur yang tidak didukung browser. Dengan menggunakan progressive enhancement, saya memastikan hanya kode yang relevan yang dimuat. Selain itu, karena dengan HTTP/2, permintaan menjadi murah, pola ini akan berfungsi dengan baik untuk banyak aplikasi, meskipun Anda mungkin ingin mempertimbangkan bundler untuk aplikasi yang sangat besar.
Aplikasi mungkin terlihat sedikit berbeda di setiap browser karena tidak semua platform mendukung semua fitur, tetapi fungsi intinya selalu ada—yang ditingkatkan secara progresif sesuai dengan kemampuan browser tertentu. Perhatikan bahwa kemampuan ini dapat berubah bahkan di satu browser yang sama, bergantung pada apakah aplikasi berjalan sebagai aplikasi yang diinstal atau di tab browser.
Jika Anda tertarik dengan aplikasi Fugu Greetings, cari dan fork di GitHub.
Tim Chromium bekerja keras untuk meningkatkan kualitas Fugu API lanjutan. Dengan menerapkan progressive enhancement dalam pengembangan aplikasi, saya memastikan bahwa semua orang mendapatkan pengalaman dasar pengukuran yang baik dan solid, tetapi orang yang menggunakan browser yang mendukung lebih banyak API platform Web mendapatkan pengalaman yang lebih baik. Kami menantikan apa yang Anda lakukan dengan progressive enhancement di aplikasi Anda.
Ucapan terima kasih
Saya berterima kasih kepada Christian Liebel dan
Hemanth HM yang telah berkontribusi dalam Salam Fugu.
Artikel ini ditinjau oleh Joe Medley dan
Kayce Basques.
Jake Archibald membantu saya mencari tahu situasi
dengan import()
dinamis dalam konteks pekerja layanan.