Creare per browser moderni e migliorare progressivamente come nel 2003
Data di pubblicazione: 29 giugno 2020
Nel marzo 2003, Nick Finck e Steve Champeon hanno stupito il mondo del web design con il concetto di miglioramento progressivo, una strategia per il web design che enfatizza il caricamento iniziale dei contenuti principali della pagina web, per poi aggiungere progressivamente livelli di presentazione e funzionalità più sfumati e tecnicamente rigorosi sopra i contenuti. Nel 2003, il miglioramento progressivo riguardava l'utilizzo di funzionalità CSS moderne, JavaScript non intrusivo e persino Scalable Vector Graphics. Il miglioramento progressivo nel 2020 e negli anni successivi consiste nell'utilizzare le funzionalità dei browser moderni.
JavaScript moderno
A proposito di JavaScript, la situazione del supporto dei browser per le funzionalità JavaScript ES 2015 di base più recenti è ottima. Il nuovo standard include promesse,
moduli, classi, template letterali, funzioni freccia, let e const,
parametri predefiniti, generatori, assegnazione destrutturante, rest e spread,
Map/Set, WeakMap/WeakSet e molti altri.
Sono tutti supportati.
Le funzioni asincrone, una funzionalità ES 2017 e una delle mie preferite,
possono essere utilizzate
in tutti i principali browser.
Le parole chiave async e await consentono di scrivere un comportamento asincrono basato su promesse
in uno stile più pulito, evitando la necessità di configurare esplicitamente le catene di promesse.
Anche le aggiunte di linguaggio ES 2020 più recenti, come l'optional chaining e l'operatore di coalescenza null, hanno raggiunto il supporto molto rapidamente. Per quanto riguarda le funzionalità principali di JavaScript, non si può chiedere di meglio.
Ad esempio:
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
console.log(adventurer.dog?.name);
// Expected output: undefined
console.log(0 ?? 42);
// Expected output: 0
L'app di esempio: Fugu Greetings
Per questo documento, lavoro con una PWA chiamata Fugu Greetings (GitHub). Il nome di questa app è un omaggio al progetto Fugu 🐡, un'iniziativa per dare al web tutte le funzionalità delle applicazioni Android, iOS e desktop. Puoi scoprire di più sul progetto nella relativa pagina di destinazione.
Fugu Greetings è un'app di disegno che ti consente di creare biglietti di auguri virtuali e inviarli ai tuoi cari. Rappresenta un esempio dei concetti chiave delle PWA. È affidabile e completamente abilitato offline, quindi anche se non hai una rete, puoi comunque utilizzarlo. È anche installabile nella schermata Home di un dispositivo e si integra perfettamente con il sistema operativo come applicazione autonoma.
Potenziamento progressivo
Ora che abbiamo chiarito questo aspetto, è il momento di parlare del miglioramento progressivo. Il glossario di MDN Web Docs definisce il concetto come segue:
Il miglioramento progressivo è una filosofia di progettazione che fornisce una base di contenuti e funzionalità essenziali al maggior numero possibile di utenti, offrendo la migliore esperienza possibile solo agli utenti dei browser più moderni che possono eseguire tutto il codice richiesto.
Il rilevamento delle funzionalità viene generalmente utilizzato per determinare se i browser possono gestire funzionalità più moderne, mentre i polyfill vengono spesso utilizzati per aggiungere funzionalità mancanti con JavaScript.
[…]
Il miglioramento progressivo è una tecnica utile che consente agli sviluppatori web di concentrarsi sullo sviluppo dei migliori siti web possibili, facendo in modo che funzionino su più user agent sconosciuti. Il degrado controllato è correlato, ma non è la stessa cosa ed è spesso visto come un'operazione che va nella direzione opposta al miglioramento progressivo. In realtà, entrambi gli approcci sono validi e spesso possono essere complementari.
Collaboratori di MDN
Creare ogni biglietto di auguri da zero può essere davvero complicato.
Perché non creare una funzionalità che consenta agli utenti di importare un'immagine e iniziare da lì?
Con un approccio tradizionale, avresti utilizzato un elemento
<input type=file>
per farlo.
Innanzitutto, devi creare l'elemento, impostare il relativo type su 'file' e aggiungere i tipi MIME
alla proprietà accept, quindi "fare clic" in modo programmatico e ascoltare le modifiche. Quando selezioni un'immagine, questa viene importata direttamente sul canvas.
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();
});
};
Quando è presente una funzionalità di importazione, probabilmente dovrebbe esserci anche una funzionalità di esportazione
in modo che gli utenti possano salvare i propri biglietti di auguri in locale.
Il modo tradizionale per salvare i file è creare un link di ancoraggio
con un attributo download
e con un URL blob come href.
Inoltre, dovresti "fare clic" in modo programmatico per attivare il download
e, per evitare perdite di memoria, non dimenticare di revocare l'URL dell'oggetto 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();
};
Ma aspetta un attimo. Mentalmente, non hai "scaricato" un biglietto di auguri, ma lo hai "salvato". Anziché mostrare una finestra di dialogo "Salva" che ti consente di scegliere dove inserire il file, il browser ha scaricato direttamente il biglietto di auguri senza interazione dell'utente e lo ha inserito direttamente nella cartella Download. Non è il massimo.
E se ci fosse un modo migliore? Cosa succederebbe se potessi semplicemente aprire un file locale, modificarlo e salvare le modifiche, in un nuovo file o nel file originale che avevi aperto inizialmente? Ebbene sì. L'API File System Access consente di aprire e creare file e directory, nonché di modificarli e salvarli .
Quindi, come faccio a rilevare le funzionalità di un'API?
L'API File System Access espone un nuovo metodo window.chooseFileSystemEntries().
Di conseguenza, devo caricare in modo condizionale diversi moduli di importazione ed esportazione a seconda che questo metodo sia disponibile.
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'),
]);
}
};
Ma prima di entrare nei dettagli dell'API File System Access, vorrei evidenziare rapidamente il pattern di miglioramento progressivo. Sui browser che non supportano l'API File System Access, carico gli script legacy.
Tuttavia, su Chrome, un browser che supporta l'API, vengono caricati solo i nuovi script.
Ciò è reso possibile in modo elegante grazie al
import() dinamico, che tutti i browser moderni
supportano.
Come ho detto prima, l'erba è piuttosto verde di questi tempi.
API File System Access
Ora che ho affrontato questo argomento, è il momento di esaminare l'implementazione effettiva basata sull'API File System Access.
Per importare un'immagine, chiamo window.chooseFileSystemEntries()
e passo una proprietà accepts in cui indico che voglio file immagine.
Sono supportate sia le estensioni dei file sia i tipi MIME.
In questo modo ottengo un handle del file, da cui posso recuperare il file effettivo chiamando 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);
}
};
L'esportazione di un'immagine è quasi la stessa, ma questa volta
devo passare un parametro di tipo 'save-file' al metodo chooseFileSystemEntries().
Da qui viene visualizzata una finestra di dialogo di salvataggio del file.
Con il file aperto, questa operazione non era necessaria, poiché 'open-file' è l'impostazione predefinita.
Ho impostato il parametro accepts in modo simile a prima, ma questa volta limitato alle sole immagini PNG.
Anche in questo caso ricevo un handle del file, ma anziché ottenere il file,
questa volta creo un flusso scrivibile chiamando createWritable().
Poi scrivo il blob, ovvero l'immagine del biglietto di auguri, nel file.
Infine, chiudo lo stream scrivibile.
Tutto può sempre fallire: lo spazio sul disco potrebbe essere esaurito,
potrebbe verificarsi un errore di scrittura o lettura oppure l'utente potrebbe semplicemente annullare la finestra di dialogo del file.
Per questo motivo, inserisco sempre le chiamate in un'istruzione 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);
}
};
Utilizzando il miglioramento progressivo con l'API File System Access, posso aprire un file come prima. Il file importato viene disegnato direttamente sulla tela. Posso apportare le modifiche e salvarle con una finestra di dialogo di salvataggio reale, in cui posso scegliere il nome e la posizione di archiviazione del file. Ora il file è pronto per essere conservato per l'eternità.
API Web Share e Web Share Target

Oltre a conservarla per l'eternità, magari voglio anche condividere la mia cartolina di auguri. Questo è qualcosa che mi consentono di fare l'API Web Share e l'API Web Share Target. I sistemi operativi mobile e, più di recente, desktop hanno acquisito meccanismi di condivisione integrati.
Ad esempio, il foglio di condivisione di Safari per computer su macOS viene attivato quando un utente fa clic su Condividi articolo sul mio blog. Potresti condividere un link all'articolo con un amico utilizzando l'app Messaggi di macOS.
Per farlo, chiamo navigator.share() e gli trasmetto title, text e url facoltativi in un oggetto.
Ma se voglio allegare un'immagine? Il livello 1 dell'API Web Share non lo supporta ancora.
La buona notizia è che il livello 2 della condivisione web ha aggiunto funzionalità di condivisione dei 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);
}
Ti mostro come farlo funzionare con l'applicazione per biglietti di auguri Fugu.
Innanzitutto, devo preparare un oggetto data con un array files composto da un blob, quindi
un title e un text. Successivamente, come best practice, utilizzo il nuovo metodo navigator.canShare(), che fa
ciò che suggerisce il nome:
mi dice se l'oggetto data che sto cercando di condividere può tecnicamente essere condiviso dal browser.
Se navigator.canShare() mi dice che i dati possono essere condivisi, sono pronto a
chiamare navigator.share() come prima.
Poiché tutto può fallire, utilizzo di nuovo un blocco 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);
}
};
Come prima, utilizzo il miglioramento progressivo.
Se esistono sia 'share' che 'canShare' nell'oggetto navigator, solo allora vado avanti e
carico share.mjs utilizzando import() dinamico.
Sui browser come Safari mobile che soddisfano solo una delle due condizioni, non carico
la funzionalità.
const loadShare = () => {
if ('share' in navigator && 'canShare' in navigator) {
import('./share.mjs');
}
};
In Fugu Greetings, se tocco il pulsante Condividi su un browser supportato come Chrome su Android, si apre il foglio di condivisione integrato. Ad esempio, posso scegliere Gmail e il widget di composizione delle email viene visualizzato con l'immagine allegata.
API Contact Picker
Poi voglio parlare dei contatti, ovvero della rubrica di un dispositivo o dell'app di gestione dei contatti. Quando scrivi un biglietto di auguri, potrebbe non essere sempre facile scrivere correttamente il nome di qualcuno. Ad esempio, ho un amico di nome Sergey che preferisce che il suo nome venga scritto in caratteri cirillici. Sto usando una tastiera QWERTZ tedesca e non ho idea di come digitare il suo nome. Questo è un problema che può essere risolto dall'API Contact Picker. Poiché ho memorizzato il mio amico nell'app Contatti dello smartphone, utilizzando l'API Contacts Picker, posso accedere ai miei contatti dal web.
Innanzitutto, devo specificare l'elenco delle proprietà a cui voglio accedere.
In questo caso, mi interessano solo i nomi,
ma per altri casi d'uso potrei essere interessato a numeri di telefono, email, icone
avatar o indirizzi fisici.
Poi, configuro un oggetto options e imposto multiple su true, in modo da poter selezionare più di una voce.
Infine, posso chiamare navigator.contacts.select(), che restituisce le proprietà ideali per i contatti selezionati dall'utente.
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);
}
};
Ormai avrai capito il pattern: carico il file solo quando l'API è effettivamente supportata.
if ('contacts' in navigator) {
import('./contacts.mjs');
}
In Fugu Greeting, quando tocco il pulsante Contatti e seleziono i miei due migliori amici, Сергей Михайлович Брин e 劳伦斯·爱德华·"拉里"·佩奇, puoi vedere come il selettore di contatti è limitato alla visualizzazione dei soli nomi, ma non degli indirizzi email o di altre informazioni come i numeri di telefono. I loro nomi vengono poi disegnati sul mio biglietto di auguri.
API Asynchronous Clipboard
A seguire, la copia e l'incolla. Una delle nostre operazioni preferite come sviluppatori di software è il copia e incolla. In qualità di autore di biglietti di auguri, a volte potrei voler fare lo stesso. Potrei voler incollare un'immagine in un biglietto di auguri a cui sto lavorando o copiare il biglietto di auguri per continuare a modificarlo da un'altra posizione. L'API Async Clipboard supporta sia testo che immagini. Ti mostrerò come ho aggiunto il supporto per il copia e incolla all'app Fugu Greetings.
Per copiare qualcosa negli appunti di sistema, devo scriverci.
Il metodo navigator.clipboard.write() accetta un array di elementi degli appunti come
parametro.
Ogni elemento degli appunti è essenzialmente un oggetto con un blob come valore e il tipo del blob come chiave.
const copy = async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
} catch (err) {
console.error(err.name, err.message);
}
};
Per incollare, devo scorrere gli elementi degli appunti che ottengo chiamando
navigator.clipboard.read().
Il motivo è che potrebbero essere presenti più elementi negli appunti in
rappresentazioni diverse.
Ogni elemento degli appunti ha un campo types che indica i tipi MIME delle risorse disponibili.
Chiamo il metodo getType() dell'elemento degli appunti, passando il
tipo MIME che ho ottenuto in precedenza.
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);
}
};
Ormai è quasi superfluo dirlo. Posso farlo solo sui browser supportati.
if ('clipboard' in navigator && 'write' in navigator.clipboard) {
import('./clipboard.mjs');
}
Come funziona in pratica? Ho un'immagine aperta nell'app Anteprima di macOS e la copio negli appunti. Quando faccio clic su Incolla, l'app Fugu Greetings mi chiede se voglio consentire all'app di vedere testo e immagini negli appunti.
Infine, dopo aver accettato l'autorizzazione, l'immagine viene incollata nell'applicazione. Funziona anche al contrario. Fammi copiare un biglietto di auguri negli appunti. Quando apro Anteprima e faccio clic su File e poi su Nuovo da Appunti, il biglietto di auguri viene incollato in una nuova immagine senza titolo.
API Badging
Un'altra API utile è l'API Badging.
In quanto PWA installabile, Fugu Greetings ha ovviamente un'icona dell'app
che gli utenti possono posizionare nel dock delle app o nella schermata Home.
Un modo divertente per dimostrare l'API è utilizzarla in Fugu Greetings,
come contatore di tratti di penna.
Ho aggiunto un listener di eventi che incrementa il contatore dei tratti di penna ogni volta che
si verifica l'evento pointerdown e poi imposta il badge dell'icona aggiornato.
Ogni volta che la tela viene cancellata, il contatore viene reimpostato e il badge viene rimosso.
let strokes = 0;
canvas.addEventListener('pointerdown', () => {
navigator.setAppBadge(++strokes);
});
clearButton.addEventListener('click', () => {
strokes = 0;
navigator.setAppBadge(strokes);
});
Questa funzionalità è un miglioramento progressivo, quindi la logica di caricamento è la solita.
if ('setAppBadge' in navigator) {
import('./badge.mjs');
}
In questo esempio, ho disegnato i numeri da 1 a 7 utilizzando un tratto di penna per ogni numero. Il contatore dei badge sull'icona ora è a sette.
API Periodic Background Sync
Vuoi iniziare ogni giorno con qualcosa di nuovo? Una funzionalità interessante dell'app Fugu Greetings è che può ispirarti ogni mattina con una nuova immagine di sfondo per iniziare il tuo biglietto di auguri. L'app utilizza l'API Periodic Background Sync per raggiungere questo obiettivo.
Il primo passo consiste nel registrare un evento di sincronizzazione periodica nella registrazione del service worker. È in ascolto di un tag di sincronizzazione chiamato 'image-of-the-day'
e ha un intervallo minimo di un giorno,
in modo che l'utente possa ricevere una nuova immagine di sfondo ogni 24 ore.
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);
}
};
Il secondo passaggio consiste nell'ascoltare l'evento periodicsync nel service worker.
Se il tag evento è 'image-of-the-day', ovvero quello registrato in precedenza,
l'immagine del giorno viene recuperata con la funzione getImageOfTheDay()
e il risultato viene propagato a tutti i client, in modo che possano aggiornare le loro tele e
cache.
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,
});
});
})()
);
}
});
Anche in questo caso si tratta di un miglioramento progressivo, quindi il codice viene caricato solo quando
l'API è supportata dal browser.
Ciò vale sia per il codice client sia per il codice del service worker.
Sui browser non supportati, nessuno dei due viene caricato.
Nota come nel service worker, anziché un import() dinamico
(che non è ancora supportato in un contesto service worker
),
utilizzo il classico
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');
}
In Fugu Greetings, se premi il pulsante Sfondo, viene visualizzata l'immagine del biglietto di auguri del giorno, che viene aggiornata ogni giorno con l'API Periodic Background Sync.
API Notification Triggers
A volte, anche con molta ispirazione, hai bisogno di un piccolo aiuto per finire un biglietto di auguri iniziato. Si tratta di una funzionalità abilitata dall'API Notification Triggers. Come utente, posso inserire un orario in cui voglio ricevere un promemoria per completare il mio biglietto di auguri. Quando arriverà il momento, riceverò una notifica che mi avvisa che il biglietto di auguri è in attesa.
Dopo aver richiesto l'ora di destinazione,
l'applicazione pianifica la notifica con un showTrigger.
Può essere un TimestampTrigger con la data target selezionata in precedenza.
La notifica del promemoria verrà attivata localmente, senza bisogno di rete o lato 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),
});
}
Come per tutto il resto che ho mostrato finora, si tratta di un miglioramento progressivo, quindi il codice viene caricato solo in modo condizionale.
if ('Notification' in window && 'showTrigger' in Notification.prototype) {
import('./notification_triggers.mjs');
}
Quando seleziono la casella di controllo Promemoria in Fugu Greetings, mi viene chiesto quando voglio ricevere un promemoria per completare il biglietto di auguri.
Quando viene attivata una notifica pianificata in Fugu Greetings, viene visualizzata come qualsiasi altra notifica, ma come ho scritto prima, non richiedeva una connessione di rete.
API Wake Lock
Voglio includere anche l'API Wake Lock. A volte devi solo fissare lo schermo abbastanza a lungo finché l'ispirazione non ti bacia. Il peggio che può succedere è che lo schermo si spenga. L'API Wake Lock può impedirlo.
Il primo passaggio consiste nell'ottenere un wake lock con navigator.wakelock.request method().
Passo la stringa 'screen' per ottenere un wakelock dello schermo.
Aggiungo poi un listener di eventi per ricevere una notifica quando il wake lock viene rilasciato.
Ciò può verificarsi, ad esempio, quando cambia la visibilità della scheda.
Se ciò accade, quando la scheda torna visibile, posso riacquisire il wake lock.
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);
Sì, si tratta di un miglioramento progressivo, quindi devo caricarlo solo quando il browser supporta l'API.
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
import('./wake_lock.mjs');
}
In Fugu Greetings, c'è una casella di controllo Insonnia che, se selezionata, mantiene lo schermo attivo.
API Idle Detection
A volte, anche se fissi lo schermo per ore, non riesci a trovare la minima idea su cosa fare con il tuo biglietto di auguri. L'API Idle Detection consente all'app di rilevare il tempo di inattività dell'utente. Se l'utente rimane inattivo per troppo tempo, l'app torna allo stato iniziale e cancella il canvas. Questa API è protetta dall'autorizzazione per le notifiche, poiché molti casi d'uso di produzione del rilevamento dell'inattività sono correlati alle notifiche, ad esempio per inviare una notifica solo a un dispositivo che l'utente sta utilizzando attivamente.
Dopo aver verificato che l'autorizzazione per le notifiche sia concessa, istanzio il detector di inattività. Registro un listener di eventi che ascolta le modifiche dello stato di inattività, che include l'utente e lo stato dello schermo. L'utente può essere attivo o inattivo e lo schermo può essere sbloccato o bloccato. Se l'utente è inattivo, il canvas viene cancellato. Assegno al rilevatore di inattività una soglia di 60 secondi.
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,
});
Come sempre, carico questo codice solo quando il browser lo supporta.
if ('IdleDetector' in window) {
import('./idle_detection.mjs');
}
Nell'app Fugu Greetings, il canvas viene cancellato quando la casella di controllo Effimero è selezionata e l'utente è inattivo per troppo tempo.
Chiusura
Uff, che corsa. Tante API in una sola app di esempio. E ricorda, non faccio mai pagare all'utente il costo del download per una funzionalità non supportata dal browser. Utilizzando il potenziamento progressivo, mi assicuro che venga caricato solo il codice pertinente. Poiché con HTTP/2 le richieste sono economiche, questo pattern dovrebbe funzionare bene per molte applicazioni, anche se potresti prendere in considerazione un bundler per le app molto grandi.
L'app potrebbe avere un aspetto leggermente diverso su ogni browser, poiché non tutte le piattaforme supportano tutte le funzionalità, ma la funzionalità di base è sempre presente, migliorata progressivamente in base alle funzionalità del browser specifico. Queste funzionalità possono cambiare anche nello stesso browser, a seconda che l'app venga eseguita come app installata o in una scheda del browser.
Puoi creare un fork di Fugu su GitHub.
Il team di Chromium sta lavorando duramente per migliorare la situazione per quanto riguarda le API Fugu avanzate. Applicando il miglioramento progressivo durante la creazione della mia app, mi assicuro che tutti ottengano un'esperienza di base buona e solida, ma che le persone che utilizzano browser che supportano più API della piattaforma web ottengano un'esperienza ancora migliore. Non vedo l'ora di scoprire come utilizzerai il miglioramento progressivo nelle tue app.
Ringraziamenti
Sono grato a Christian Liebel e
Hemanth HM, che hanno contribuito a Fugu Greetings.
Questo documento è stato esaminato da Joe Medley e
Kayce Basques.
Jake Archibald mi ha aiutato a capire la situazione
con import() dinamico nel contesto di un service worker.