Applicazione dei principi di programmazione delle mini app a un progetto di esempio

Il dominio dell'app

Per mostrare il modo di programmare di una mini app applicato a un'app web, avevo bisogno di un'idea per un'app piccola ma abbastanza completa. L'allenamento a intervalli ad alta intensità (HIIT) è una strategia di allenamento cardiovascolare che alterna serie di brevi periodi di allenamento anaerobico intenso con periodi di recupero meno intensi. Molti allenamenti di HIIT utilizzano timer per l'allenamento, ad esempio questa sessione online di 30 minuti del canale YouTube The Body Coach TV.

Sessione online di allenamento HIIT con timer ad alta intensità verde.
Periodo attivo.
Sessione online di allenamento HIIT con timer rosso a bassa intensità.
Periodo di riposo.

Esempio di app per l'allenamento HIIT

Per questo capitolo, ho creato un esempio base di un'applicazione per timer HIIT chiamata "HIIT Time", che consente all'utente di definire e gestire vari timer, sempre costituiti da un intervallo di alta e una bassa intensità, quindi di selezionarne uno per una sessione di allenamento. Si tratta di un'app adattabile con una barra di navigazione, una barra delle schede e tre pagine:

  • Esercizio: la pagina attiva durante un esercizio. Consente all'utente di selezionare uno dei timer e presenta tre suonerie di avanzamento: il numero di serie, il periodo attivo e il periodo di riposo.
  • Timer:gestisce i timer esistenti e consente all'utente di crearne di nuovi.
  • Preferenze: consente di attivare/disattivare gli effetti sonori e l'output vocale e di selezionare la lingua e il tema.

Gli screenshot seguenti danno un'idea dell'applicazione.

App di esempio con l'ora HIIT in modalità verticale.
Scheda "Allenamento" del tempo HIIT in modalità verticale.
App di esempio con tempo HIIT in modalità Orizzontale.
Scheda "Allenamento" del tempo HIIT in modalità Orizzontale.
App di esempio HIIT che mostra la gestione di un timer.
Gestione del timer del tempo HIIT.

Struttura dell'app

Come spiegato in precedenza, l'app è composta da una barra di navigazione, una barra delle schede e tre pagine disposte in una griglia. Navbar e tabbar sono realizzate come iframe con un contenitore <div> intermedio e altri tre iframe per le pagine, di cui uno è sempre visibile e dipende dalla selezione attiva nella barra delle schede. Per le pagine in-app create dinamicamente viene pubblicato un iframe finale che rimanda a about:blank, necessarie per modificare i timer esistenti o crearne di nuovi. Lo chiamo questo pattern di app a pagina singola (MPSPA) su più pagine.

Visualizzazione di Chrome DevTools della struttura HTML dell&#39;app, che mostra che è composta da sei iframe: uno per la barra di navigazione, uno per la barra delle schede e tre raggruppati per ogni pagina dell&#39;app, con un iframe segnaposto finale per le pagine dinamiche.
L'app è composta da sei iframe.

Markup lit-html basato sui componenti

La struttura di ogni pagina è realizzata come un'infrastruttura lit-html valutata dinamicamente in fase di runtime. Per uno sfondo su lit-html, si tratta di una libreria di modelli HTML efficiente, espressiva ed estensibile per JavaScript. Utilizzandolo direttamente nei file HTML, il modello di programmazione mentale è orientato direttamente all'output. In qualità di programmatore, scrivi un modello di come apparirà l'output finale e lit-html riempie le lacune in modo dinamico in base ai dati e collega i listener di eventi. L'app utilizza elementi personalizzati di terze parti, come Stringhe per scarpe <sl-progress-ring>, o un elemento personalizzato implementato automaticamente chiamato <human-duration>. Poiché gli elementi personalizzati hanno un'API dichiarativa (ad esempio, l'attributo percentage dell'anello di avanzamento), funzionano bene insieme a lit-html, come puoi vedere nell'elenco di seguito.

<div>
  <button class="start" @click="${eventHandlers.start}" type="button">
    ${strings.START}
  </button>
  <button class="pause" @click="${eventHandlers.pause}" type="button">
    ${strings.PAUSE}
  </button>
  <button class="reset" @click="${eventHandlers.reset}" type="button">
    ${strings.RESET}
  </button>
</div>

<div class="progress-rings">
  <sl-progress-ring
    class="sets"
    percentage="${Math.floor(data.sets/data.activeTimer.sets*100)}"
  >
    <div class="progress-ring-caption">
      <span>${strings.SETS}</span>
      <span>${data.sets}</span>
    </div>
  </sl-progress-ring>
</div>
Tre pulsanti e un anello dei progressi.
Sezione visualizzata della pagina corrispondente al markup in alto.

Modello di programmazione

Ogni pagina ha una classe Page corrispondente che riempie il markup lit-html con "life", fornendo implementazioni dei gestori di eventi e fornendo i dati per ogni pagina. Questa classe supporta anche metodi del ciclo di vita come onShow(), onHide(), onLoad() e onUnload(). Le pagine hanno accesso a un datastore che consente la condivisione dello stato per pagina e dello stato globale persistenti facoltativamente. Tutte le stringhe sono gestite centralmente, quindi l'internazionalizzazione è incorporata. Il routing è gestito dal browser sostanzialmente senza costi, poiché l'app fa solo che attivi/disattiva la visibilità dell'iframe e per le pagine create dinamicamente cambia l'attributo src dell'iframe segnaposto. L'esempio seguente mostra il codice per chiudere una pagina creata dinamicamente.

import Page from '../page.js';

const page = new Page({
  eventHandlers: {
    back: (e) => {
      e.preventDefault();
      window.top.history.back();
    },
  },
});
Pagina in-app realizzata come un iframe.
La navigazione avviene dall'iframe all'iframe.

Stili

Lo stile delle pagine avviene a livello di pagina nel relativo file CSS con ambito. Questo significa che solitamente gli elementi possono essere gestiti direttamente tramite i relativi nomi, poiché non possono verificarsi conflitti con altre pagine. Gli stili globali vengono aggiunti a ogni pagina, quindi non è necessario dichiarare ripetutamente impostazioni centrali come font-family o box-sizing. Qui vengono definiti anche temi e opzioni per la modalità Buio. L'elenco seguente mostra le regole per la pagina Preferenze che dispone i vari elementi del modulo su una griglia.

main {
  max-width: 600px;
}

form {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-gap: 0.5rem;
  margin-block-end: 1rem;
}

label {
  text-align: end;
  grid-column: 1 / 2;
}

input,
select {
  grid-column: 2 / 3;
}
Pagina delle preferenze dell&#39;app HIIT Time che mostra un modulo in layout a griglia.
Ogni pagina ha il suo mondo. Lo stile avviene direttamente con i nomi degli elementi.

Riattiva schermo

Durante un esercizio, lo schermo non deve spegnersi. Sui browser che la supportano, l'impostazione HIIT Time lo determina tramite un Wakelock dello schermo. Lo snippet seguente mostra come si fa.

if ('wakeLock' in navigator) {
  const requestWakeLock = async () => {
    try {
      page.shared.wakeLock = await navigator.wakeLock.request('screen');
      page.shared.wakeLock.addEventListener('release', () => {
        // Nothing.
      });
    } catch (err) {
      console.error(`${err.name}, ${err.message}`);
    }
  };
  // Request a screen wake lock…
  await requestWakeLock();
  // …and re-request it when the page becomes visible.
  document.addEventListener('visibilitychange', async () => {
    if (
      page.shared.wakeLock !== null &&
      document.visibilityState === 'visible'
    ) {
      await requestWakeLock();
    }
  });
}

Testare l'applicazione

L'applicazione del tempo HIIT è disponibile su GitHub. Puoi giocare con la demo in una nuova finestra o direttamente nell'incorporamento dell'iframe riportato di seguito, che simula un dispositivo mobile.

Ringraziamenti

Questo articolo è stato esaminato da Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent e Keith Gu.