Anwendung der Programmierprinzipien für Mini-Apps auf ein Beispielprojekt

Die App-Domain

Um zu zeigen, wie die Programmierung im Mini-App-Format auf eine Webanwendung angewendet wird, brauchte ich eine kleine, aber ausreichend vollständige App-Idee. Intervalltraining mit hoher Intensität (HIIT) ist eine Strategie für Ausdauertraining, bei der kurze Phasen intensiver anaerober Bewegung mit weniger intensiven Erholungsphasen abgewechselt werden. Bei vielen HIIT-Trainings werden HIIT-Timer verwendet, z. B. bei dieser 30-minütigen Onlinesitzung vom YouTube-Kanal The Body Coach TV.

Online-HIIT-Trainingseinheit mit grünem Timer für hohe Intensität.
Aktiver Zeitraum.
Online-HIIT-Trainingseinheit mit rotem Timer für niedrige Intensität.
Ruhezeit.

Beispiel-App „HIIT Time“

Für dieses Kapitel habe ich ein einfaches Beispiel für eine solche HIIT-Timer-App erstellt, die treffend den Namen „HIIT Time“ trägt. Mit dieser App können Nutzer verschiedene Timer definieren und verwalten, die immer aus einem Intervall mit hoher und einem mit niedriger Intensität bestehen, und dann einen davon für eine Trainingseinheit auswählen. Es ist eine responsive App mit einer Navigationsleiste, einer Tableiste und drei Seiten:

  • Training: Die aktive Seite während eines Trainings. Der Nutzer kann einen der Timer auswählen. Es gibt drei Fortschrittsringe: die Anzahl der Sätze, die aktive Zeit und die Ruhezeit.
  • Timer: Verwaltet vorhandene Timer und ermöglicht es Nutzern, neue zu erstellen.
  • Einstellungen: Hier können Sie Soundeffekte und Sprachausgabe aktivieren oder deaktivieren und Sprache und Design auswählen.

Die folgenden Screenshots vermitteln einen Eindruck von der Anwendung.

Beispiel-App für HIIT-Zeit im Porträtmodus
Tab „Workout“ bei der HIIT-Zeit im Porträtmodus.
Beispiel-App „HIIT Time“ im Querformat
Tab „Training“ im HIIT-Training im Querformat.
Beispiel-App „HIIT Time“ mit Timerverwaltung
HIIT-Zeit-Timer-Management.

App-Struktur

Wie oben beschrieben, besteht die App aus einer Navigationsleiste, einer Tab-Leiste und drei Seiten, die in einem Raster angeordnet sind. Navigationsleiste und Tab-Leiste sind als Iframes mit einem <div>-Container dazwischen mit drei weiteren Iframes für die Seiten implementiert. Einer davon ist immer sichtbar und hängt von der aktiven Auswahl in der Tab-Leiste ab. Ein abschließender iFrame, der auf about:blank verweist, dient für dynamisch erstellte In-App-Seiten, die zum Ändern vorhandener Timer oder zum Erstellen neuer Timer erforderlich sind. Ich nenne dieses Muster mehrseitige Single-Page-App (MPSPA).

Ansicht der Chrome-Entwicklertools der HTML-Struktur der App mit sechs iFrames: einem für die Navigationsleiste, einem für die Tableiste und drei gruppierten iFrames für jede Seite der App mit einem endgültigen Platzhalter-iFrame für dynamische Seiten.
Die Anwendung besteht aus sechs iFrames.

Komponentenbasiertes lit-html-Markup

Die Struktur jeder Seite wird als lit-html-Scaffold realisiert, das zur Laufzeit dynamisch ausgewertet wird. Lit-HTML ist eine effiziente, ausdrucksstarke und erweiterbare HTML-Template-Bibliothek für JavaScript. Durch die direkte Verwendung in den HTML-Dateien ist das mentale Programmiermodell direkt ausgabeorientiert. Als Programmierer schreiben Sie eine Vorlage für die endgültige Ausgabe. Lit-HTML füllt dann die Lücken dynamisch basierend auf Ihren Daten aus und verbindet die Ereignis-Listener. Die App verwendet benutzerdefinierte Elemente von Drittanbietern wie <sl-progress-ring> von Shoelace oder ein selbst implementiertes benutzerdefiniertes Element namens <human-duration>. Da benutzerdefinierte Elemente eine deklarative API haben (z. B. das Attribut percentage des Fortschrittsrings), funktionieren sie gut mit Lit-HTML, wie in der folgenden Liste dargestellt.

<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>
Drei Tasten und ein Fortschrittsring.
Der gerenderte Bereich der Seite, der dem obigen Markup entspricht.

Programmiermodell

Jede Seite hat eine entsprechende Page-Klasse, die das Lit-HTML-Markup mit Leben füllt, indem sie Implementierungen der Ereignishandler und die Daten für jede Seite bereitstellt. Diese Klasse unterstützt auch Lebenszyklusmethoden wie onShow(), onHide(), onLoad() und onUnload(). Seiten haben Zugriff auf einen Datenspeicher, der zum Teilen des optional gespeicherten Zustands pro Seite und des globalen Zustands dient. Alle Strings werden zentral verwaltet, sodass die Internationalisierung bereits integriert ist. Das Routing wird vom Browser im Grunde kostenlos ausgeführt, da die App lediglich die Sichtbarkeit des Iframes umschaltet und für dynamisch erstellte Seiten das src-Attribut des Platzhalter-Iframes ändert. Das folgende Beispiel zeigt den Code zum Schließen einer dynamisch erstellten Seite.

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

const page = new Page({
  eventHandlers: {
    back: (e) => {
      e.preventDefault();
      window.top.history.back();
    },
  },
});
In-App-Seite als iFrame implementiert.
Die Navigation erfolgt von iFrame zu iFrame.

Stile

Das Stylen von Seiten erfolgt pro Seite in einer eigenen CSS-Datei. Das bedeutet, dass Elemente in der Regel direkt über ihre Elementnamen angesprochen werden können, da keine Überschneidungen mit anderen Seiten auftreten können. Globale Stile werden jeder Seite hinzugefügt, sodass zentrale Einstellungen wie font-family oder box-sizing nicht wiederholt deklariert werden müssen. Hier werden auch die Designs und die Optionen für den dunklen Modus definiert. Im folgenden Listeneintrag sind die Regeln für die Seite „Einstellungen“ zu sehen, auf der die verschiedenen Formularelemente in einem Raster angeordnet sind.

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;
}
Seite mit den Einstellungen der App „HIIT Time“ mit einem Formular im Rasterlayout
Jede Seite ist eine eigene Welt. Das Styling erfolgt direkt über die Elementnamen.

Display-Wakelock

Während eines Trainings sollte der Bildschirm nicht ausgeschaltet werden. In Browsern, die dies unterstützen, wird dies von HIIT Time durch eine Bildschirmsperre realisiert. Das folgende Snippet zeigt, wie es geht.

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();
    }
  });
}

Anwendung testen

Die HIIT Time-Anwendung ist auf GitHub verfügbar. Sie können die Demo in einem neuen Fenster oder direkt im eingebetteten iFrame unten ausprobieren, der ein Mobilgerät simuliert.

Danksagungen

Dieser Artikel wurde von Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent und Keith Gu gelesen.