Un approccio non reattivo alla creazione di app web cross-device

Boris smus
Boris Smus

Le query supporti sono fantastiche, ma...

Le query supporti sono fantastiche, una meraviglia per gli sviluppatori di siti web che vogliono apportare piccole modifiche ai loro fogli di stile per offrire un'esperienza migliore agli utenti su dispositivi di varie dimensioni. Le query supporti ti consentono essenzialmente di personalizzare il CSS del tuo sito a seconda delle dimensioni dello schermo. Prima di approfondire questo articolo, scopri di più sul responsive design e dai un'occhiata ad alcuni ottimi esempi di utilizzo delle query supporti qui: mediaqueri.es.

Come sottolinea Brad Frost in un articolo precedente, cambiare l'aspetto è solo uno dei molti aspetti da considerare quando si crea per il web mobile. Se l'unica azione che fai quando crei il tuo sito web per dispositivi mobili è personalizzare il layout con query supporti, c'è la seguente situazione:

  • Tutti i dispositivi dispongono dello stesso JavaScript, degli stessi CSS e degli stessi asset (immagini, video), il che comporta tempi di caricamento più lunghi del necessario.
  • Tutti i dispositivi ricevono lo stesso DOM iniziale, costringendo gli sviluppatori a scrivere codice CSS eccessivamente complesso.
  • Poca flessibilità per specificare interazioni personalizzate su misura per ciascun dispositivo.

Le app web hanno bisogno di più di query supporti

Non fraintendere. Non odio il reattore tramite query supporti e credo che abbia una sua collocazione a livello globale. Inoltre, alcuni dei problemi menzionati in precedenza possono essere risolti con approcci come le immagini adattabili, il caricamento degli script dinamici e così via. Tuttavia, a un certo punto, potresti trovarti a fare troppe modifiche incrementali e potrebbe essere meglio pubblicare versioni diverse.

Con l'aumento della complessità delle UI che crei e la tendenza delle app web a pagina singola, dovrai fare di più per personalizzare le UI per ogni tipo di dispositivo. Questo articolo ti insegnerà a eseguire queste personalizzazioni con uno sforzo minimo. L'approccio generale prevede di classificare il dispositivo del visitatore nella giusta classe di dispositivi e di pubblicare la versione appropriata per quel dispositivo, massimizzando al contempo il riutilizzo del codice tra le versioni.

Quali classi di dispositivi vuoi scegliere come target?

Esistono tantissimi dispositivi connessi a Internet e quasi tutti hanno un browser. La complicazione risiede nella loro diversità: laptop Mac, workstation Windows, iPhone, iPad, telefoni Android con input touch, rotelline di scorrimento, tastiere, input vocale, dispositivi con sensibilità alla pressione, smartwatch, tostapane, frigoriferi e molti altri. Alcuni di questi dispositivi sono onnipresenti, mentre altri sono molto rari.

Una varietà di dispositivi.
Una varietà di dispositivi (fonte).

Per creare una buona esperienza utente, devi sapere chi sono i tuoi utenti e quali dispositivi utilizzano. Se crei un'interfaccia utente per un utente desktop con mouse e tastiera e la passi a un utente di smartphone, la tua interfaccia sarà frustrante perché è progettata per schermi di dimensioni diverse e modalità di immissione diverse.

Esistono due estremità dello spettro di approcci:

  1. Crea una versione compatibile con tutti i dispositivi. L'esperienza utente ne risentirà, poiché i vari dispositivi hanno considerazioni di progettazione diverse.

  2. Crea una versione per ogni dispositivo che vuoi supportare. Questa operazione richiederà sempre, perché creerai troppe versioni della tua applicazione. Inoltre, quando arriverà il prossimo nuovo smartphone (il che avviene all'incirca ogni settimana), sarai costretto a creare un'altra versione.

C'è un compromesso fondamentale: maggiore è il numero di categorie di dispositivi a disposizione, migliore sarà l'esperienza utente che potrai offrire, ma più lavoro sarà necessario per progettare, implementare e gestire.

Creare una versione separata per ogni classe di dispositivi scelta può essere una buona idea per motivi legati alle prestazioni o se le versioni che vuoi pubblicare per classi di dispositivi diverse variano enormemente. Per il resto, il responsive web design è un approccio perfettamente ragionevole.

Una potenziale soluzione

Esiste un compromesso: classifica i dispositivi in categorie e progetta la migliore esperienza possibile per ogni categoria. Le categorie che scegli dipendono dal prodotto e dall'utente target. Ecco un esempio di classificazione che include i più diffusi dispositivi abilitati per il web.

  1. schermi piccoli + touch (principalmente telefoni)
  2. schermi grandi + touch (principalmente tablet)
  3. schermi grandi + tastiera/mouse (principalmente computer/laptop)

Questa è solo una delle tante possibili suddivisioni, ma una che ha molto senso al momento della stesura di questo documento. Nell'elenco precedente mancano i dispositivi mobili senza touchscreen (ad es. feature phone, alcuni lettori di ebook dedicati). Tuttavia, nella maggior parte di questi è installato un software per la navigazione da tastiera o uno screen reader, che è adatto a chi realizza il sito tenendo in considerazione l'accessibilità.

Esempi di app web specifiche per fattore di forma

Esistono molti esempi di proprietà web che pubblicano versioni completamente diverse a seconda dei fattori di forma. A farlo la Ricerca Google e Facebook. Alcune considerazioni includono il rendimento (recupero di asset, il rendering delle pagine) e l'esperienza utente più generale.

Nel mondo delle app native, molti sviluppatori scelgono di personalizzare l'esperienza per una classe di dispositivi. Ad esempio, Flipboard per iPad ha un'interfaccia utente molto diversa rispetto a Flipboard per iPhone. La versione per tablet è ottimizzata per l'uso con due mani e la possibilità di capovolgere in orizzontale, mentre la versione del telefono è pensata per l'interazione con una sola mano e la possibilità di capovolgere in verticale. Molte altre applicazioni iOS offrono inoltre versioni molto diverse per telefoni e tablet, ad esempio Cose (elenco di cose da fare) e Showyou (video social), descritte di seguito:

Notevole personalizzazione dell'interfaccia utente per telefono e tablet.
Significativa personalizzazione dell'interfaccia utente per smartphone e tablet.

Approccio n. 1: rilevamento lato server

Sul server, abbiamo una comprensione molto più limitata del dispositivo con cui abbiamo a che fare. Probabilmente l'indizio più utile disponibile è la stringa dello user agent, che viene fornita tramite l'intestazione User-Agent su ogni richiesta. Per questo motivo, lo stesso approccio di sniffing in UA funzionerà. I progetti DeviceAtlas e WURFL lo fanno già (e forniscono molte informazioni aggiuntive sul dispositivo).

Sfortunatamente, ognuna di queste presenta problemi specifici. WURFL è molto grandi e contiene 20 MB di XML, il che potrebbe comportare un notevole overhead lato server per ogni richiesta. Esistono progetti che suddividono il codice XML per motivi di prestazioni. DeviceAtlas non è open source e per l'utilizzo richiede una licenza a pagamento.

Esistono anche alternative più semplici e senza costi, come il progetto Detect Mobile Browsers. Lo svantaggio, ovviamente, è che il rilevamento dei dispositivi sarà inevitabilmente meno completo. Inoltre, distingue solo tra dispositivi mobili e non, fornendo un supporto limitato per i tablet solo attraverso una serie di modifiche ad hoc.

Approccio n. 2: rilevamento lato client

Possiamo ottenere molte informazioni sul browser e sul dispositivo dell'utente utilizzando il rilevamento delle funzionalità. Dobbiamo stabilire se il dispositivo dispone della funzionalità touch e se si tratta di uno schermo piccolo o grande.

Dobbiamo tracciare una linea da qualche parte per distinguere i dispositivi con touchscreen piccolo e grande. E le cover Edge come il Galaxy Note da 5"? L'immagine che segue mostra una serie di dispositivi Android e iOS popolari sovrapposti (con le risoluzioni dello schermo corrispondenti). L'asterisco indica che il dispositivo ha una densità doppia. Sebbene la densità dei pixel possa essere raddoppiata, il CSS segnala sempre le stesse dimensioni.

Una breve occhiata ai pixel in CSS: i pixel CSS sul web mobile non sono gli stessi dei pixel dello schermo. I dispositivi iOS retina hanno introdotto la pratica di raddoppiare la densità dei pixel (ad es. iPhone 3GS rispetto a 4, iPad 2 e 3). Le UA di Safari per dispositivi mobili retina segnalano ancora la stessa larghezza del dispositivo per evitare di violare il web. Come altri dispositivi (ad es. Android) hanno schermi con risoluzione maggiore, ma utilizzano lo stesso stratagemma relativo alla larghezza del dispositivo.

Risoluzione del dispositivo (in pixel).
Risoluzione del dispositivo (in pixel).

A complicare questa decisione, tuttavia, è importante considerare sia la modalità verticale che orizzontale. Non vogliamo ricaricare la pagina o caricare script aggiuntivi ogni volta che riorientiamo il dispositivo, anche se è consigliabile eseguire il rendering della pagina in modo diverso.

Nel seguente diagramma, i quadrati rappresentano le dimensioni massime di ciascun dispositivo a seguito della sovrapposizione dei contorni verticale e orizzontale (e completamento del quadrato):

Risoluzione verticale + orizzontale (in pixel)
Risoluzione verticale + orizzontale (in pixel)

Se imposti la soglia su 650px, classifichiamo iPhone, Galaxy Nexus come smalltouch e iPad e Galaxy Tab a "tablet". In questo caso, la Galaxy Note androgino è classificata come "telefono" e avrà il layout del telefono.

Quindi, una strategia ragionevole potrebbe essere la seguente:

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

Guarda un esempio minimo di approccio per il rilevamento delle funzionalità in azione.

L'approccio alternativo consiste nell'utilizzare lo sniffing UA per rilevare il tipo di dispositivo. In sostanza, crei un insieme di euristiche e le confrontiamo con navigator.userAgent dell'utente. Lo pseudocodice ha un aspetto simile al seguente:

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

Guarda un esempio dell'approccio di rilevamento di UA in azione.

Nota sul caricamento lato client

Se esegui il rilevamento UA sul tuo server, puoi decidere quali CSS, JavaScript e DOM pubblicare quando ricevi una nuova richiesta. Tuttavia, se esegui il rilevamento lato client, la situazione è più complessa. Sono disponibili diverse opzioni:

  1. Reindirizza a un URL specifico del tipo di dispositivo che contiene la versione per questo tipo di dispositivo.
  2. Carica in modo dinamico gli asset specifici per il tipo di dispositivo.

Il primo approccio è diretto e richiede un reindirizzamento come window.location.href = '/tablet'. Tuttavia, alla posizione verranno aggiunte le informazioni sul tipo di dispositivo, quindi puoi utilizzare l'API History per ripulire l'URL. Purtroppo questo approccio prevede un reindirizzamento, che può essere lento, in particolare sui dispositivi mobili.

Il secondo approccio è un po' più complesso da implementare. Hai bisogno di un meccanismo per caricare dinamicamente CSS e JS e (a seconda del browser) potresti non essere in grado di eseguire operazioni come la personalizzazione di <meta viewport>. Inoltre, dato che non c'è reindirizzamento, rimani bloccato con il codice HTML originale che è stato pubblicato. Ovviamente puoi manipolarlo con JavaScript, ma il processo potrebbe essere lento e/o poco elegante, a seconda dell'applicazione.

Decisione del client o del server

Questi sono i compromessi tra gli approcci:

Cliente Pro:

  • È più a prova di futuro perché si basa sulle dimensioni/funzionalità degli schermi piuttosto che su UA.
  • Non è necessario aggiornare costantemente l'elenco UA.

Server Pro:

  • Controllo completo della versione da pubblicare su quali dispositivi.
  • Prestazioni migliori: nessun reindirizzamento del client o caricamento dinamico.

Preferisco iniziare con il rilevamento di device.js e lato client. Man mano che l'applicazione si evolve, se ritieni che il reindirizzamento lato client sia uno svantaggio significativo delle prestazioni, puoi facilmente rimuovere lo script device.js e implementare il rilevamento UA sul server.

Novità: device.js

Device.js è il punto di partenza per eseguire il rilevamento dei dispositivi semantico e basato su query multimediali senza bisogno di una configurazione lato server speciale, risparmiando il tempo e l'impegno necessari per l'analisi delle stringhe dello user agent.

L'idea è quella di fornire nella parte superiore di <head> un markup ottimizzato per i motori di ricerca (link rel=alternate) che indica le versioni del sito che vuoi fornire.

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

Successivamente, puoi eseguire il rilevamento UA lato server e gestire il reindirizzamento della versione autonomamente oppure utilizzare lo script device.js per eseguire il reindirizzamento lato client basato sulle funzionalità.

Per ulteriori informazioni, consulta la pagina del progetto device.js e anche un'applicazione falsa che utilizza device.js per il reindirizzamento lato client.

Consiglio: MVC con viste specifiche per i fattori di forma

A questo punto potresti pensare che ti sto dicendo di creare tre app completamente separate, una per ogni tipo di dispositivo. No! La condivisione del codice è la chiave.

Speriamo che tu stia utilizzando un framework simile a MVC, come Backbone, Ember e così via. In questo caso, conosci già il principio di separazione dei problemi, in particolare il principio secondo cui l'interfaccia utente (livello di visualizzazione) deve essere disaccoppiata dalla logica (livello modello). Se è la prima volta, inizia con alcune di queste risorse su MVC e MVC in JavaScript.

La storia cross-device si adatta perfettamente al framework MVC esistente. Puoi spostare facilmente le visualizzazioni in file separati, creando una visualizzazione personalizzata per ogni tipo di dispositivo. Poi puoi pubblicare lo stesso codice per tutti i dispositivi, tranne il livello di visualizzazione.

MVC cross-device.
MVC cross-device.

Il tuo progetto potrebbe avere la seguente struttura (ovviamente puoi scegliere la struttura più adatta a seconda della tua applicazione):

modelli/ (modelli condivisi) item.js item-collection.js

controller/ (controller condivisi) item-controller.js

versioni/ (dispositivi specifici del dispositivo) tablet/ desktop/ telefono/ (codice specifico del telefono) style.css index.html views/ item.js item-list.js

Questo tipo di struttura consente di controllare completamente quali asset vengono caricati in ogni versione, dal momento che disponi di codice HTML, CSS e JavaScript personalizzato per ogni dispositivo. Si tratta di una funzionalità molto potente e può portare al modo più snelli e performante di sviluppo per il web cross-device, senza fare affidamento su trucchi come le immagini adattive.

Dopo aver eseguito il tuo strumento di creazione preferito, devi concatenare e minimizzare tutto il codice JavaScript e CSS in singoli file per un caricamento più rapido, con il codice HTML di produzione simile al seguente (per i telefoni, utilizzando device.js):

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

Tieni presente che la query supporti (touch-enabled: 0) non è standard (implementata solo in Firefox con il prefisso del fornitore moz), ma viene gestita correttamente (grazie a Modernizr.touch) da device.js.

Override della versione

Il rilevamento dei dispositivi a volte può non funzionare e, in alcuni casi, un utente potrebbe preferire visualizzare il layout del tablet sul proprio telefono (ad esempio se sta usando un Galaxy Note), quindi è importante offrire agli utenti la possibilità di scegliere la versione del sito da utilizzare se vogliono eseguire l'override manuale.

L'approccio tipico è fornire un link alla versione desktop dalla versione mobile. È abbastanza facile da implementare, ma device.js supporta questa funzionalità con il parametro GET device.

In conclusione

Riassumendo, quando crei UI a pagina singola cross-device che non si adattano perfettamente al reattivo design:

  1. Scegli un insieme di classi di dispositivi da supportare e criteri in base ai quali classificare i dispositivi in classi.
  2. Crea la tua app MVC con una netta separazione dei problemi, suddividendo le visualizzazioni dal resto del codebase.
  3. Utilizzare device.js per eseguire il rilevamento della classe del dispositivo lato client.
  4. Quando è tutto pronto, pacchettizza lo script e i fogli di stile in una combinazione di ciascuna classe di dispositivo.
  5. Se le prestazioni del reindirizzamento lato client sono un problema, abbandona device.js e passa al rilevamento UA lato server.