Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
vuoi
o PayPal
tutte le volte che vuoi
Struttura del progetto:
ProjFolder
|
|____ css
| |___ style.css
| ⚙️
|_____ js
| |___ app.js
|
|_____ index.html
|
|_____ serviceWorker.js
/js/app.js
// Il navigator rappresenta il browser
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/serviceworker.js")
.then(function(registration) {
console.log("Service Worker registrato con scope:", registration.scope)
}).catch(function(error) {
console.log("Registrazione Service Worker fallita:", error);
})
} /serviceWorker.js
// Aggiungiamo un listener che catturi tutti gli eventi fetch.
// In questo modo, le fetch saranno indirizzate al Service Worker, non
// alle normali pagine, e racchiuderanno le richieste fatte dal browser
self.addEventListener("fetch", function(event) {
console.log("Richiesta fetch per:", event.request.url);
// Intercettiamo una richiesta col Service Worker e restituiamone una nuova
if (event.request.url.includes("style.css")) {
event.respondWith(
new Response(
'h1 { color: red; font-size: 4em; }',
{headers: {'Content-Type': 'text/css'}}
)
);
}
});
// Creiamo un proxy identità: restituisce la risorsa richiesta
// ma se siamo offline, viene restituita un'altra risposta
self.addEventListener("fetch", function(event) {
event.respondWith(
fetch(event.request)
.catch(function() {
return new Response("Sei offline.")
})
);
}); Ogni Service Worker ha uno scope limitato che può controllare.
Consideriamo di avere un’app in foo.com e 2 sottodomini foo.com/A e foo.com/B
Potremmo inserire un Service Worker in foo.com/A per dirottare tutte le richieste fatte verso
foo.com/B
Lo scope è dato dalla directory che ospita il Service Worker:
- Se è “/” → allora controlla tutte le richieste che originano in qualche parte dell’app
- Se è “/js/” → allora controlla le richieste che originano soltanto nella directory /js
Quando registriamo un Service Worker, possiamo passare l’attributo scope per limitare lo
scope stesso:
navigator.serviceWorker.register("/serviceWorker.js", { scope: "/A" });
Supponiamo adesso di avere una pagina index-offline.html
Vogliamo servire questa pagina quando l’utente è offline.
Idealmente scriveremmo:
self.addEventListener("fetch", function(event) {
event.respondWith(
fetch(event.request)
.catch(function() {
return fetch("/index-offline.html")
})
);
});
Tuttavia, tale approccio non funzionerebbe:
1. La fetch si effettua quando si è online
2. Si memorizza la risorsa localmente (sul device)
3. Si restituisce la risorsa quando si è online
Per risolvere tale problema possiamo utilizzare la CacheStorage API.
Il CacheStorage è un layer di caching:
- Non è la cache del browser
- Non è la vecchia Application Cache
Tale API espone dei metodi di base, con cui:
- Creare e manipolare diverse cache
- Memorizzare dati nelle cache
- Recuperare dati dalle cache
- Eliminare vecchi dati
La combinazione di Service Worker e CacheStorage permette di far funzionare l’app offline.
In questo modo si ha un controllo programmatico su cosa viene memorizzato, quando viene
memorizzato, e così via.
Per fare caching, catturiamo l’evento install.
Tale evento avviene una sola volta, subito dopo il register e subito prima dell’attivazione.
Permette di fare caching di tutti i file che vogliamo rendere disponibili offline.
È utile anche per arrestare l’attivazione di un Service Worker nel caso in cui vi siano
problemi:
- Se qualcosa va storto durante il caching, possiamo arrestare l’attivazione
- Alla prossima richiesta, il browser riproverà ad attivare il Service Worker
// Memorizzazione
self.addEventListener("install", function(event) {
event.waitUntil(
caches.open("la-mia-cache")
.then(function(cache) {
return cache.add("/index-offline.html");
})
);
});
- Il metodo waitUntil() estende il tempo di vita dell’evento fino a quando la Promise
passata come argomento non si realizza.
- L’oggetto caches è l’endpoint della CacheStorage API
- Il metodo caches.open(X) permette di accedere ad una cache col nome X (o la crea
se non esiste), restituendo una Promise che - se realizzata - ci permette appunto di
accedere alla cache
- Il metodo cache.add() aggiunge il file alla cache
>> La cache è a tutti gli effetti una mappa, la chiave dell’elemento sarà il nome usato nel
metodo cache.add()
// Recupero
self.addEventListener("fetch", function(event) {
event.respondWith(
fetch(event.request)
.catch(function() {
return caches.match("/index-offline.html");
})
);
});
Se una qualunque richiesta non va a buon fine, restituiamo la versione cache di
index-offline.html
- Il metodo caches.match(X) verifica l’esistenza della chiave X in una qualunque cache
creata dalla nostra web app; se vogliamo accedere ad una cache specifica (ad
esempio, la-mia-cache) si utilizza il metodo caches.open():
caches.open("la-mia-cache")
.then(function(cache) {
return cache.match("/index-offline.html");
});
Per fare il caching di più risorse, utilizziamo il metodo cache.addAll
var CACHED_URLS = [
"/index-offline.html",
"/css/style.css",
];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("la-mia-cache")
.then((cache) => {
return cache.addAll(CACHED_URLS);
})
);
});
Finora abbiamo recuperato indiscriminatamente una sola risorsa.
Per recuperare la risorsa effettivamente richiesta, dobbiamo effettuare un matching
specifico.
self.addEventListener("fetch", (event) => {
event.respondWith(
fetch(event.request)
.catch(() => {
return caches.match(event.request)
.then((response) => {
if (response)
return response;
else if (event.request.headers.get("accept").includes("text/html"))
return caches.match("/index-offline.html");
})
})
);
}); >> La Promise di caches.match() non viene mai rigetta, quindi controlliamo esplicitamente
se la risposta è valida.
Se non è valida, la richiesta accetta risposte con contenuto del tipo text/html, e restituiamo
quindi il nostro index-offline.html
React Native
React Native è un framework per sviluppare applicazioni mobile in JavaScript.
Utilizza la libreria React, creata da Meta e originariamente utilizzata per sviluppare UI e web
app.
React Native compila JavaScript in vere applicazioni native, che abbiano accesso a
platform-specific API e componenti.
In principio, la filosofia dietro la realizzazione delle mobile app era quella di utilizzare
JavaScript per la loro creazione, in modo da facilitare il lavoro per chi arrivava dal mondo del
web development.
L’app mobile veniva quindi scritta in JavaScript, e poi wrappata (racchiusa) in un sandbox
browser chiamato WebView, sfruttando tool come Apache Cordova, ottenendo app ibride.
In questo modo era possibile scrivere codice una sola volta ed eseguirlo ovunque (“Write
once, run everywhere”), ma le app mobile ottenute non erano altro che siti web racchiusi in
una WebView.
Col tempo la filosofia si è evoluta, fino ad arrivare alle moderne app cross-platform.
Tale approccio si basa sui seguenti punti:
- Utilizzare un modello fissato che permetta di creare applicazioni web
- Associare gli elementi di questo modello ai componenti nativi mobile
Questo è l’approccio seguito da React Native, che permette di scrivere codice usando i
componenti della libreria React (usata per le UI delle web app), per poi convertirli in
componenti nativi dei SO mobile.
>> Non possiamo trasporre direttamente una web app in React in una mobile app, ma
possiamo scriverla in React Native senza passare per altri linguaggi.
Il funzionamento di React Native si basa sul JavaScript bridge.
A runtime tutti i componenti che usiamo in React Native saranno renderizzati col
corrispettivo componente nativo della piattaforma target.
Ad esempio, il componente <View> in React Native verrà tradotto in:
- View in Android
- UIView in iOS
- <div> in HTML
Ciò ci permette di realizzare 3 tipologie diverse di app a partire da una singola codebase.
Pro Contro
Usato in production da moltissime realtà Immaturo rispetto a framework come Flutter
Community enorme Necessità di conoscere React
Performance È comunque un’astrazione costruita sulle API
delle piattaforme target, se queste cambiano,
Tooling React Native deve essere aggiornato d’accordo
Osserviamo come funziona il framework sotto al cofano.
La libreria React è presente nell’engine JavaScriptCore.
React Native comunica asincronicamente con l’OS mobile.
Questa comunicazione permette l’accesso alle API per i widget nativi (ad esempio, i
componenti)
React Native renderizza questi componenti tramite queste API, e questo vale per ogni
dispositivo target.
I componenti React Native vengono tradotti nei rispettivi componenti target attraverso 2
livelli:
1. JavaScript Thread
- Contiene una JavaScript virtual machine, detta Hermes
- Esegue il codice, invoca le API native del target, …
- Vi è un singolo thread, nel quale sarà eseguita la componente logica dell’app
- L’app è pacchettizzata in un singolo file tramite Metro, che traduce anche il
codice JSX in JS
2. Native Thread
- Qui si esegue il codice nativo
- React Native implementa questa parte di codice nativo per ogni piattaforma
(ad esempio, Java per Android)
- È composto da moduli che comunicano con l’SDK Android o iOS
- Espone una API che unifica le varie piattaforma, che può essere invocata
dallo JavaScript Thread
La comunicazione tra JavaScript Thread e Native Thread avviene per mezzo di un bridge
- Risolvere un problema di matematica
- Riassumere un testo
- Tradurre una frase
- E molto altro ancora...
Per termini, condizioni e privacy, visita la relativa pagina.